diff options
author | Kenneth Anthony Giusti <kgiusti@apache.org> | 2010-03-23 18:00:49 +0000 |
---|---|---|
committer | Kenneth Anthony Giusti <kgiusti@apache.org> | 2010-03-23 18:00:49 +0000 |
commit | f7e909f3ec86b1330c74abc9014103c1201eb502 (patch) | |
tree | a064c60f5aec628f9ccf532fab5659f44c6ddf4c | |
parent | 759668f3c17006cf3f8bd13fed3003fee1e98dae (diff) | |
download | qpid-python-f7e909f3ec86b1330c74abc9014103c1201eb502.tar.gz |
merge r919024:926606 trunk into this branch
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/qmf-devel0.7a@926686 13f79535-47bb-0310-9956-ffa450edef68
236 files changed, 5013 insertions, 1763 deletions
diff --git a/qpid/QPID_VERSION.txt b/qpid/QPID_VERSION.txt new file mode 100644 index 0000000000..eb49d7c7fd --- /dev/null +++ b/qpid/QPID_VERSION.txt @@ -0,0 +1 @@ +0.7 diff --git a/qpid/cpp/.gitignore b/qpid/cpp/.gitignore new file mode 100644 index 0000000000..4e945f96db --- /dev/null +++ b/qpid/cpp/.gitignore @@ -0,0 +1 @@ +/QPID_VERSION.txt diff --git a/qpid/cpp/BuildInstallSettings.cmake b/qpid/cpp/BuildInstallSettings.cmake index 60d6b14e11..c1cf2bcba2 100644 --- a/qpid/cpp/BuildInstallSettings.cmake +++ b/qpid/cpp/BuildInstallSettings.cmake @@ -20,8 +20,17 @@ # Settings related to the Qpid build and install CMake/CTest/CPack procedure.
# These are used by both the C++ and WCF components.
-set (QPID_VERSION_MAJOR 0)
-set (QPID_VERSION_MINOR 6)
+# Parse the version from QPID_VERSION.txt. +# Use the top level qpid/ file if we're in an SVN checkout, source dir otherwise. +if(EXISTS "${PROJECT_SOURCE_DIR}/../QPID_VERSION.txt") + file(READ "${PROJECT_SOURCE_DIR}/../QPID_VERSION.txt" QPID_VERSION) +elseif (EXISTS "${PROJECT_SOURCE_DIR}/QPID_VERSION.txt") + file(READ "${PROJECT_SOURCE_DIR}/QPID_VERSION.txt" QPID_VERSION) +else() + message(FATAL_ERROR "Cannot find QPID_VERSION.txt") +endif(EXISTS "${PROJECT_SOURCE_DIR}/../QPID_VERSION.txt") +string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\n" "\\1" QPID_VERSION_MAJOR "${QPID_VERSION}") +string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\n" "\\2" QPID_VERSION_MINOR "${QPID_VERSION}") # When doing installs, there are a number of components that the item can
# be associated with. Since there may be different sets of components desired
diff --git a/qpid/cpp/CMakeLists.txt b/qpid/cpp/CMakeLists.txt index 8c4aa06ee2..485ead127a 100644 --- a/qpid/cpp/CMakeLists.txt +++ b/qpid/cpp/CMakeLists.txt @@ -43,37 +43,6 @@ if (WIN32) set (CPACK_NSIS_URL_INFO_ABOUT "http://qpid.apache.org/") # Needs this to correctly set up Start menu links later. set (CPACK_PACKAGE_EXECUTABLES "") - - # The WCF/C++ client is built separately (it doesn't build via CMake) - # but is installed with the C++ components on Windows. - install (PROGRAMS - ${CMAKE_SOURCE_DIR}/../wcf/src/Apache/Qpid/Channel/bin/Release/Apache.Qpid.Channel.dll - ${CMAKE_SOURCE_DIR}/../wcf/src/Apache/Qpid/Channel/bin/Release/Apache.Qpid.Interop.dll - DESTINATION ${QPID_INSTALL_LIBDIR} - COMPONENT ${QPID_COMPONENT_CLIENT}) - install (DIRECTORY ${CMAKE_SOURCE_DIR}/../wcf/samples/Channel - DESTINATION ${QPID_INSTALL_EXAMPLESDIR} - COMPONENT ${QPID_COMPONENT_EXAMPLES} - PATTERN ".svn" EXCLUDE) - - # Find where gacutil is; it's not usually on the default PATH. If it can't - # be located, the WCF assemblies can't be inserted in the GAC. - # The gacutil stuff is disabled for now; to re-enable it, one must be able - # to either find the gacutil somehow and specify the directory where - # gacutil lives, add it to PATH, or use the FUSE API in a program that - # inserts the DLLs programatically without gacutil. Or use WiX, which can - # supposedly do this in an easier way. - # - # See jira QPID-2310 for more info; update that if you change this. - #set (CPACK_NSIS_EXTRA_INSTALL_COMMANDS " - # ExecWait 'gacutil -I \\\"$INSTDIR\\\\${QPID_INSTALL_LIBDIR}\\\\Apache.Qpid.Channel.dll\\\"' - # ExecWait 'gacutil -I \\\"$INSTDIR\\\\${QPID_INSTALL_LIBDIR}\\\\Apache.Qpid.Interop.dll\\\"' - # ") - #set (CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " - # ExecWait 'gacutil /u \\\"Apache.Qpid.Channel\\\"' - # ExecWait 'gacutil /u \\\"Apache.Qpid.Interop\\\"' - # ") - endif (WIN32) set (QPIDC_CONF_FILE ${QPID_INSTALL_CONFDIR}/qpidc.conf CACHE STRING diff --git a/qpid/cpp/INSTALL b/qpid/cpp/INSTALL index d54d2affc3..9977c13201 100644 --- a/qpid/cpp/INSTALL +++ b/qpid/cpp/INSTALL @@ -114,6 +114,8 @@ The optional clustering packages changed name in Fedora 10. On Fedora 9 or earli # yum install openais-devel cman-devel On Fedora 10 or later # yum install corosync-devel cmanlib-devel +On Fedora 12 they changed again: + # yum install corosynclib-devel clusterlib-devel For SASL and SSL, include # yum install cyrus-sasl-devel diff --git a/qpid/cpp/Makefile.am b/qpid/cpp/Makefile.am index dcbc4c10c3..5c475946a0 100644 --- a/qpid/cpp/Makefile.am +++ b/qpid/cpp/Makefile.am @@ -25,7 +25,7 @@ ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = \ LICENSE NOTICE README SSL RELEASE_NOTES DESIGN \ xml/cluster.xml INSTALL-WINDOWS CMakeLists.txt BuildInstallSettings.cmake \ - packaging/NSIS + packaging/NSIS QPID_VERSION.txt SUBDIRS = managementgen etc src docs/api docs/man examples bindings/qmf diff --git a/qpid/cpp/SSL b/qpid/cpp/SSL index 4f80e77479..e7f040c76c 100644 --- a/qpid/cpp/SSL +++ b/qpid/cpp/SSL @@ -13,16 +13,16 @@ providing the ssl.so module is loaded): SSL Settings: --ssl-use-export-policy Use NSS export policy - --ssl-cert-password-file PATH File containing password to use for - accessing certificate database + --ssl-cert-password-file PATH File containing password to use for accessing + certificate database --ssl-cert-db PATH Path to directory containing certificate database - --ssl-cert-name NAME (thinkpad) Name of the certificate to use - --ssl-port PORT (5671) Port on which to listen for SSL - connections - --ssl-require-client-authentication Forces clients to authenticate in order + --ssl-cert-name NAME (hostname) Name of the certificate to use + --ssl-port PORT (5671) Port on which to listen for SSL connections + --ssl-require-client-authentication Forces clients to authenticate in order to establish an SSL connection - + --ssl-sasl-no-dict Disables SASL mechanisms that are vulner able to + passive dictionary-based password attacks The first four of these are also available as client options (where they must either be in the client config file or set as environment @@ -66,6 +66,12 @@ and run e.g. ./src/tests/perftest --count 10000 -P ssl --port 5671 \ --broker myhost.mydomain +When authentication is enabled, the EXTERNAL mechanism will be +available on client authenticated SSL connections. This allows the +clients authorisation id to be taken from the validated client +certificate (it will be the CN with any DCs present appended as the +domain, e.g. CN=bob,DC=acme,DC=com would result in an identity of +bob@acme.com). [1] http://www.mozilla.org/projects/security/pki/nss/ref/ssl/gtstd.html [2] http://www.mozilla.org/projects/security/pki/nss/tools/certutil.html diff --git a/qpid/cpp/bld-winsdk.ps1 b/qpid/cpp/bld-winsdk.ps1 new file mode 100644 index 0000000000..e2fe888a3a --- /dev/null +++ b/qpid/cpp/bld-winsdk.ps1 @@ -0,0 +1,121 @@ +#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# This script requires cmake, and 7z to be already on the path devenv should be on the path as
+# a result of installing Visual Studio
+
+# Filter to extract the included files from c style #include lines
+# TODO: Not used yet
+function extractIncludes {
+ param($includedir=".", $found=@{}, $notfound=@{})
+ process {
+ # Put original files in list if not already there
+ $file = $_.FullName
+ if (!($found.Contains($file))) {
+ $found[$file] = $true;
+ }
+ $content = Get-Content $_
+ $filebase = $_.PSParentPath
+ $content | foreach {
+ if ($_ -match '^\s*#include\s*([<"])([^">]*)([>"])\s*') {
+ $included=$matches[2]
+ # Try to find the corresponding file in the same directory
+ # as the including file then try the include dir
+ $testpathf=Join-Path $filebase $included
+ $testpathi=Join-Path $includedir $included
+ if (Test-Path $testpathf) {
+ $includedfile = Get-Item $testpathf
+ } elseif (Test-Path $testpathi) {
+ $includedfile = Get-Item $testpathi
+ } else {
+ $notfound[$included] = $file
+ continue;
+ }
+ if (!($found.Contains($includedfile.FullName))) {
+ $found[$includedfile.FullName] = $file
+ $includedfile
+ }
+ }
+ }
+ }
+}
+
+function getIncludeFiles {
+ param($base, $findall=$false)
+ if ($findall) {
+ Get-ChildItem -recurse -include *.h $base
+ } else {
+ foreach ($path in $input) {
+ $full=Join-Path $base $path
+ if (Test-Path $full) {
+ Get-Item $full
+ }
+ }
+ }
+}
+
+if ($args.length -lt 1) {
+ Write-Host 'Need to specify location of qpid src tree'
+ exit
+}
+
+$qpid_src=$args[0]
+$ver=$args[1]
+if ($ver -eq $null) {
+ $qpid_version_file="$qpid_src\QPID_VERSION.txt"
+
+ if ( !(Test-Path $qpid_version_file)) {
+ Write-Host "Path doesn't seem to be a qpid src tree (no QPID_VERSION.txt)"
+ exit
+ }
+ $ver=Get-Content $qpid_version_file
+}
+
+$qpid_cpp_src="$qpid_src\cpp"
+$install_dir="install_$([System.IO.Path]::GetRandomFileName())"
+$zipfile="qpid-cpp-$ver.zip"
+
+# This assumes Visual Studio 2008
+cmake -G "Visual Studio 9 2008" "-DCMAKE_INSTALL_PREFIX=$install_dir" $qpid_cpp_src
+
+# Need to build doxygen api docs separately as nothing depends on them
+devenv qpid-cpp.sln /build "Release|Win32" /project docs-user-api
+
+# Build both debug and release so we can ship both sets of libs
+devenv qpid-cpp.sln /build "Release|Win32" /project INSTALL
+devenv qpid-cpp.sln /build "Debug|Win32" /project INSTALL
+
+# Cut down the files to put in the zip
+$removable=@(
+ 'bin/qpidd.exe', 'bin/qpidbroker*.*', 'plugins',
+ 'bin/qmfengine*.*', 'bin/qpidxarm*.*',
+ 'bin/boost_regex*.*')
+foreach ($pattern in $removable) {
+ Remove-Item -recurse "$install_dir/$pattern"
+}
+
+# It would be very good to cut down on the shipped boost include files too, ideally by
+# starting with the qpid files and recursively noting all boost headers actually needed
+
+# Create a new zip
+if (Test-Path $zipfile) {Remove-Item $zipfile}
+&'7z' a $zipfile ".\$install_dir\*"
+
+# Remove temporary install area
+# Remove-Item -recurse $install_dir
diff --git a/qpid/cpp/bootstrap b/qpid/cpp/bootstrap index 5f33fec63f..925344acf7 100755 --- a/qpid/cpp/bootstrap +++ b/qpid/cpp/bootstrap @@ -1,5 +1,8 @@ #!/bin/sh -set -e + +# Copy the global QPID_VERSION.txt file into the source tree. +cp ../QPID_VERSION.txt . + aclocal -I m4 autoheader libtoolize --automake @@ -15,10 +18,10 @@ cat > src/managementgen.mk <<EOF \$(mgen_cmd) EOF - automake autoconf +# Optionally do the build as well. if [ "$1" = "-build" -o "$1" = "--build" ] ; then shift ./configure "$@" diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac index 8c8efc1917..adcf4c5dd2 100644 --- a/qpid/cpp/configure.ac +++ b/qpid/cpp/configure.ac @@ -8,10 +8,13 @@ dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. dnl -dnl When updating the name/version number here, also update it in -dnl src/qpid/Version.h -AC_INIT([qpidc], [0.7], [dev@qpid.apache.org]) +# Pick up the version from QPID_VERSION.txt in the qpid/cpp source tree. +# NB. You need to re-run bootstrap if the global qpid/QPID_VERSION.txt changes. +AC_INIT([qpidc], + [m4_esyscmd([cat QPID_VERSION.txt | tr -d '\n'])], + [dev@qpid.apache.org]) + AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects]) @@ -232,8 +235,7 @@ if test -n "$PYTHON" ; then fi AM_CONDITIONAL([HAVE_PYTHON_DEVEL], [test -f $PYTHON_INC/Python.h && test -n "$SWIG"]) - -specdir=`pwd`/$srcdir/../specs +specdir=`pwd`/$srcdir/../specs AMQP_FINAL_XML=$specdir/amqp.0-10-qpid-errata.xml AC_SUBST(AMQP_FINAL_XML) AM_CONDITIONAL([GENERATE], [test -f $AMQP_FINAL_XML]) @@ -346,6 +348,8 @@ esac test $want_xml = no && use_xml=no + + # If the user doesn't say not to use XML, see if it's available. if test $use_xml != no; then # Then see if XQilla is available @@ -364,6 +368,13 @@ if test $use_xml != no; then # Else XQilla is available - use it to build test $use_xml = yes && AC_DEFINE([HAVE_XML], [1], [Compile-in XML Exchange support.]) + + # Check to see if we need to use legacy calls for effective boolean value + xqilla_has_ebv=yes + AC_CHECK_HEADER([xqilla/ast/XQEffectiveBooleanValue.hpp], , [xqilla_has_ebv=no]) + test $xqilla_has_ebv = yes && + AC_DEFINE([XQ_EFFECTIVE_BOOLEAN_VALUE_HPP], [1], [XQilla version has xqilla/ast/XQEffectiveBooleanValue.hpp.]) + fi AM_CONDITIONAL([HAVE_XML], [test $use_xml = yes]) @@ -525,7 +536,6 @@ AC_CONFIG_FILES([ src/Makefile src/tests/Makefile src/tests/test_env.sh - src/tests/testagent/Makefile docs/man/Makefile docs/api/Makefile docs/api/user.doxygen diff --git a/qpid/cpp/docs/api/CMakeLists.txt b/qpid/cpp/docs/api/CMakeLists.txt index 1f9573b98a..5d04f85128 100644 --- a/qpid/cpp/docs/api/CMakeLists.txt +++ b/qpid/cpp/docs/api/CMakeLists.txt @@ -26,7 +26,10 @@ if (GEN_DOXYGEN) set (srcdir ${CMAKE_CURRENT_SOURCE_DIR}) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/user.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/user.doxygen) - add_custom_target (user-api-docs COMMAND ${DOXYGEN_EXECUTABLE} user.doxygen) + configure_file (${CMAKE_CURRENT_SOURCE_DIR}/developer.doxygen.in + ${CMAKE_CURRENT_BINARY_DIR}/develeoper.doxygen) + add_custom_target (docs-user-api COMMAND ${DOXYGEN_EXECUTABLE} user.doxygen) + add_custom_target (docs-developer COMMAND ${DOXYGEN_EXECUTABLE} developer.doxygen) # HTML files are generated to ./html - put those in the install. install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ diff --git a/qpid/cpp/docs/src/CONTENTS b/qpid/cpp/docs/src/CONTENTS new file mode 100644 index 0000000000..b7a9d411ac --- /dev/null +++ b/qpid/cpp/docs/src/CONTENTS @@ -0,0 +1,12 @@ +This directory contains documentation about the C++ source +that is expressed in formats that does not fit comfortably +within C++ source files. + +As with all documentation, including comments, it may become +outmoded with respect to the code. + +If you find external code doco useful in your work -- if it +helps you save some time -- please return some of that time +in the form of effort to keep the documentation updated. + + diff --git a/qpid/cpp/docs/src/DispatchHandle.odg b/qpid/cpp/docs/src/DispatchHandle.odg Binary files differnew file mode 100644 index 0000000000..c08b3a4e1a --- /dev/null +++ b/qpid/cpp/docs/src/DispatchHandle.odg diff --git a/qpid/cpp/examples/README.txt b/qpid/cpp/examples/README.txt index 0663286664..bfad478c63 100644 --- a/qpid/cpp/examples/README.txt +++ b/qpid/cpp/examples/README.txt @@ -16,12 +16,12 @@ On Linux: # ./declare_queues host1 9999
On Windows:
- C:\Program Files\qpidc-0.6\examples\direct> declare_queues host1 9999
+ C:\Program Files\qpidc-0.7\examples\direct> declare_queues host1 9999 The qpid C++ broker executable is named qpidd on Linux and qpidd.exe
on Windows. The default install locations are:
- Linux: /usr/sbin
-- Windows: C:\Program Files\qpidc-0.6\bin
+- Windows: C:\Program Files\qpidc-0.7\bin In a C++ source distribution the broker is located in the src subdirectory
(generally, from this examples directory, ../src).
@@ -52,9 +52,9 @@ On Linux: # ./listener
On Windows:
- C:\Program Files\qpidc-0.6\examples\direct> declare_queues
- C:\Program Files\qpidc-0.6\examples\direct> direct_producer
- C:\Program Files\qpidc-0.6\examples\direct> listener
+ C:\Program Files\qpidc-0.7\examples\direct> declare_queues + C:\Program Files\qpidc-0.7\examples\direct> direct_producer + C:\Program Files\qpidc-0.7\examples\direct> listener Note that there is no requirement for the listener to be running before the
messages are published. The messages are stored in the queue until consumed
@@ -86,9 +86,9 @@ On Linux: # ./fanout_producer
On Windows:
- C:\Program Files\qpidc-0.6\examples\fanout> listener
+ C:\Program Files\qpidc-0.7\examples\fanout> listener - C:\Program Files\qpidc-0.6\examples\direct> fanout_producer
+ C:\Program Files\qpidc-0.7\examples\direct> fanout_producer == Publisher/Subscriber ==
@@ -122,9 +122,9 @@ On Linux: # ./topic_publisher
On Windows:
- C:\Program Files\qpidc-0.6\examples\pub-sub> topic_listener
+ C:\Program Files\qpidc-0.7\examples\pub-sub> topic_listener - C:\Program Files\qpidc-0.6\examples\pub-sub> topic_publisher
+ C:\Program Files\qpidc-0.7\examples\pub-sub> topic_publisher == Request/Response ==
@@ -147,8 +147,8 @@ On Linux: # ./client
On Windows:
- C:\Program Files\qpidc-0.6\examples\request-response> server
- C:\Program Files\qpidc-0.6\examples\request-response> client
+ C:\Program Files\qpidc-0.7\examples\request-response> server + C:\Program Files\qpidc-0.7\examples\request-response> client == QMF Agent ==
diff --git a/qpid/cpp/examples/examples.sln b/qpid/cpp/examples/examples.sln index 18077f9efa..0d5ddd6bfe 100644 --- a/qpid/cpp/examples/examples.sln +++ b/qpid/cpp/examples/examples.sln @@ -295,6 +295,18 @@ Global {E614CC2C-FECA-1BAD-23CE-CD4095BD3C8B}.Release|Win32.Build.0 = Release|Win32
{E614CC2C-FECA-1BAD-23CE-CD4095BD3C8B}.Release|x64.ActiveCfg = Release|x64
{E614CC2C-FECA-1BAD-23CE-CD4095BD3C8B}.Release|x64.Build.0 = Release|x64
+ {D79791E5-C593-4F23-B545-0CE72D181F2A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D79791E5-C593-4F23-B545-0CE72D181F2A}.Debug|Win32.Build.0 = Debug|Win32
+ {D79791E5-C593-4F23-B545-0CE72D181F2A}.Debug|x64.ActiveCfg = Debug|Win32
+ {D79791E5-C593-4F23-B545-0CE72D181F2A}.Release|Win32.ActiveCfg = Release|Win32
+ {D79791E5-C593-4F23-B545-0CE72D181F2A}.Release|Win32.Build.0 = Release|Win32
+ {D79791E5-C593-4F23-B545-0CE72D181F2A}.Release|x64.ActiveCfg = Release|Win32
+ {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Debug|Win32.Build.0 = Debug|Win32
+ {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Debug|x64.ActiveCfg = Debug|Win32
+ {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|Win32.ActiveCfg = Release|Win32
+ {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|Win32.Build.0 = Release|Win32
+ {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/qpid/cpp/examples/messaging/client.cpp b/qpid/cpp/examples/messaging/client.cpp index 4d68d7c575..3f7afb5e3e 100644 --- a/qpid/cpp/examples/messaging/client.cpp +++ b/qpid/cpp/examples/messaging/client.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"; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); @@ -70,6 +70,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/drain.cpp b/qpid/cpp/examples/messaging/drain.cpp index bd18fd3884..38f6bdbb98 100644 --- a/qpid/cpp/examples/messaging/drain.cpp +++ b/qpid/cpp/examples/messaging/drain.cpp @@ -93,8 +93,8 @@ int main(int argc, char** argv) { Options options(argv[0]); if (options.parse(argc, argv)) { + Connection connection(options.connectionOptions); try { - Connection connection(options.connectionOptions); connection.open(options.url); Session session = connection.newSession(); Receiver receiver = session.createReceiver(options.address); @@ -116,6 +116,7 @@ int main(int argc, char** argv) return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } } return 1; diff --git a/qpid/cpp/examples/messaging/map_receiver.cpp b/qpid/cpp/examples/messaging/map_receiver.cpp index 05be4090d2..cdbae6007e 100644 --- a/qpid/cpp/examples/messaging/map_receiver.cpp +++ b/qpid/cpp/examples/messaging/map_receiver.cpp @@ -38,8 +38,8 @@ using std::string; int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Receiver receiver = session.createReceiver("message_queue"); @@ -52,6 +52,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/map_sender.cpp b/qpid/cpp/examples/messaging/map_sender.cpp index b6e0621844..037bb55201 100644 --- a/qpid/cpp/examples/messaging/map_sender.cpp +++ b/qpid/cpp/examples/messaging/map_sender.cpp @@ -37,9 +37,8 @@ using std::string; int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; - + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Sender sender = session.createSender("message_queue"); @@ -64,6 +63,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/queue_receiver.cpp b/qpid/cpp/examples/messaging/queue_receiver.cpp index 192b90088d..95756a9a3d 100644 --- a/qpid/cpp/examples/messaging/queue_receiver.cpp +++ b/qpid/cpp/examples/messaging/queue_receiver.cpp @@ -31,8 +31,8 @@ using namespace qpid::messaging; int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Receiver receiver = session.createReceiver("message_queue"); @@ -51,6 +51,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/queue_sender.cpp b/qpid/cpp/examples/messaging/queue_sender.cpp index b2535d90bf..439e1dffaf 100644 --- a/qpid/cpp/examples/messaging/queue_sender.cpp +++ b/qpid/cpp/examples/messaging/queue_sender.cpp @@ -34,8 +34,8 @@ int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; int count = argc>2 ? atoi(argv[2]) : 10; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Sender sender = session.createSender("message_queue"); @@ -50,10 +50,10 @@ int main(int argc, char** argv) { // And send a final message to indicate termination. sender.send(Message("That's all, folks!")); session.sync(); - connection.close(); return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/server.cpp b/qpid/cpp/examples/messaging/server.cpp index 0a80a5fb02..046a209e2f 100644 --- a/qpid/cpp/examples/messaging/server.cpp +++ b/qpid/cpp/examples/messaging/server.cpp @@ -41,8 +41,8 @@ using std::string; int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Receiver receiver = session.createReceiver("service_queue; {create: always}"); @@ -70,6 +70,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/spout.cpp b/qpid/cpp/examples/messaging/spout.cpp index cbb6b52b34..4819c6bc00 100644 --- a/qpid/cpp/examples/messaging/spout.cpp +++ b/qpid/cpp/examples/messaging/spout.cpp @@ -156,8 +156,8 @@ int main(int argc, char** argv) { Options options(argv[0]); if (options.parse(argc, argv)) { + Connection connection(options.connectionOptions); try { - Connection connection(options.connectionOptions); connection.open(options.url); Session session = connection.newSession(); Sender sender = session.createSender(options.address); @@ -183,6 +183,7 @@ int main(int argc, char** argv) return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } } return 1; diff --git a/qpid/cpp/examples/messaging/topic_receiver.cpp b/qpid/cpp/examples/messaging/topic_receiver.cpp index 13f881e574..9e0264a4c3 100644 --- a/qpid/cpp/examples/messaging/topic_receiver.cpp +++ b/qpid/cpp/examples/messaging/topic_receiver.cpp @@ -34,8 +34,8 @@ int main(int argc, char** argv) { const std::string url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; const std::string pattern = argc>2 ? argv[2] : "#.#"; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Receiver receiver = session.createReceiver("news_service; {filter:[control, " + pattern + "]}"); @@ -53,6 +53,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/messaging/topic_sender.cpp b/qpid/cpp/examples/messaging/topic_sender.cpp index d1ada45864..a37d4b5371 100644 --- a/qpid/cpp/examples/messaging/topic_sender.cpp +++ b/qpid/cpp/examples/messaging/topic_sender.cpp @@ -51,8 +51,8 @@ int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; int count = argc>2 ? atoi(argv[2]) : 10; + Connection connection; try { - Connection connection; connection.open(url); Session session = connection.newSession(); Sender sender = session.createSender("news_service"); @@ -72,6 +72,7 @@ int main(int argc, char** argv) { return 0; } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } return 1; } diff --git a/qpid/cpp/examples/pub-sub/verify_python_cpp.in b/qpid/cpp/examples/pub-sub/verify_python_cpp.in index 52e8db9d72..f2871eb743 100644 --- a/qpid/cpp/examples/pub-sub/verify_python_cpp.in +++ b/qpid/cpp/examples/pub-sub/verify_python_cpp.in @@ -5,6 +5,10 @@ Declaring queue: news Declaring queue: usa Declaring queue: weather Listening for messages ... +Message: That's all, folks! from europe +Message: That's all, folks! from news +Message: That's all, folks! from usa +Message: That's all, folks! from weather Message: europe.news 0 from europe Message: europe.news 0 from news Message: europe.news 1 from europe @@ -25,10 +29,6 @@ Message: europe.weather 3 from europe Message: europe.weather 3 from weather Message: europe.weather 4 from europe Message: europe.weather 4 from weather -Message: That's all, folks! from europe -Message: That's all, folks! from news -Message: That's all, folks! from usa -Message: That's all, folks! from weather Message: usa.news 0 from news Message: usa.news 0 from usa Message: usa.news 1 from news diff --git a/qpid/cpp/examples/verify b/qpid/cpp/examples/verify index 43e1206a0c..9a1ed078d6 100755 --- a/qpid/cpp/examples/verify +++ b/qpid/cpp/examples/verify @@ -19,6 +19,7 @@ # under the License. # +export LC_ALL=C # Driver script to verify installed examples (also used for build tests.) # diff --git a/qpid/cpp/examples/verify_all b/qpid/cpp/examples/verify_all index baffd422ad..9d71c7498a 100755 --- a/qpid/cpp/examples/verify_all +++ b/qpid/cpp/examples/verify_all @@ -48,7 +48,7 @@ if test -z "$exclude_regexp"; then run_examples=$all_examples else for f in $all_examples; do - { cat $f | grep $exclude_regexp > /dev/null ; } || run_examples="$run_examples $f" + { echo $f | grep $exclude_regexp > /dev/null ; } || run_examples="$run_examples $f" done fi $verify $topsrcdir $topbuilddir $run_examples diff --git a/qpid/cpp/include/qpid/agent/QmfAgentImportExport.h b/qpid/cpp/include/qpid/agent/QmfAgentImportExport.h index 9eee4a18fd..e41425a7ba 100644 --- a/qpid/cpp/include/qpid/agent/QmfAgentImportExport.h +++ b/qpid/cpp/include/qpid/agent/QmfAgentImportExport.h @@ -21,7 +21,7 @@ */ #if defined(WIN32) && !defined(QPID_DECLARE_STATIC) -#if defined(QMF_AGENT_EXPORT) || defined (qmfagent_EXPORTS) +#if defined (qmf_EXPORTS) #define QMF_AGENT_EXTERN __declspec(dllexport) #else #define QMF_AGENT_EXTERN __declspec(dllimport) diff --git a/qpid/cpp/include/qpid/client/amqp0_10/FailoverUpdates.h b/qpid/cpp/include/qpid/client/amqp0_10/FailoverUpdates.h new file mode 100644 index 0000000000..159745e67c --- /dev/null +++ b/qpid/cpp/include/qpid/client/amqp0_10/FailoverUpdates.h @@ -0,0 +1,55 @@ +#ifndef QPID_CLIENT_AMQP0_10_FAILOVERUPDATES_H +#define QPID_CLIENT_AMQP0_10_FAILOVERUPDATES_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ClientImportExport.h" + +namespace qpid { + +namespace messaging { +class Connection; +} + +namespace client { +namespace amqp0_10 { + +struct FailoverUpdatesImpl; +/** + * A utility to listen for updates on cluster membership - published + * via the amq.failover exchange - and update the list of known urls + * for a connection accordingly. + */ +class FailoverUpdates +{ + public: + QPID_CLIENT_EXTERN FailoverUpdates(qpid::messaging::Connection& connection); + QPID_CLIENT_EXTERN ~FailoverUpdates(); + private: + FailoverUpdatesImpl* impl; + + //no need to copy instances of this class + FailoverUpdates(const FailoverUpdates&); + FailoverUpdates& operator=(const FailoverUpdates&); +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_FAILOVERUPDATES_H*/ diff --git a/qpid/cpp/include/qpid/framing/FieldValue.h b/qpid/cpp/include/qpid/framing/FieldValue.h index f413d5a552..8af1f8dedd 100644 --- a/qpid/cpp/include/qpid/framing/FieldValue.h +++ b/qpid/cpp/include/qpid/framing/FieldValue.h @@ -335,6 +335,16 @@ class Str16Value : public FieldValue { QPID_COMMON_EXTERN Str16Value(const std::string& v); }; +class Var16Value : public FieldValue { + public: + QPID_COMMON_EXTERN Var16Value(const std::string& v, uint8_t code); +}; + +class Var32Value : public FieldValue { + public: + QPID_COMMON_EXTERN Var32Value(const std::string& v, uint8_t code); +}; + class Struct32Value : public FieldValue { public: QPID_COMMON_EXTERN Struct32Value(const std::string& v); diff --git a/qpid/cpp/include/qpid/messaging/Address.h b/qpid/cpp/include/qpid/messaging/Address.h index 745949792d..55befd2d6d 100644 --- a/qpid/cpp/include/qpid/messaging/Address.h +++ b/qpid/cpp/include/qpid/messaging/Address.h @@ -24,7 +24,7 @@ #include <string> #include "qpid/Exception.h" #include "qpid/messaging/Variant.h" -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" #include <ostream> namespace qpid { @@ -79,6 +79,11 @@ class AddressImpl; * nide when a sender or receiver is cancelled. Can be one of <i>always</i>, * <i>never</i>, <i>sender</i> or <i>receiver</i>.</td></tr> * + * <tr valign=top><td>reliability</td><td>indicates the level of + * reliability expected. Can be one of unreliable, at-most-once, + * at-least-once or exactly-once (the latter is not yet correctly + * supported).</td></tr> + * * <tr valign=top><td>node-properties</td><td>A nested map of properties of the addressed * entity or 'node'. These can be used when automatically creating it, * or to assert certain properties. @@ -109,16 +114,14 @@ class AddressImpl; * receiver does not want to receiver messages published to the topic * that originate from a sender on the same connection</td></tr> * - * <tr valign=top><td>browse</td><td>(only relevant for queues) specifies that the receiver - * does not wish to consume the messages, but merely browse them</td></tr> + * <tr valign=top><td>mode</td><td>(only relevant for queues) + * indicates whether the subscribe should consume (the default) or + * merely browse the messages. Valid values are 'consume' and + * 'browse'</td></tr> * * <tr valign=top><td>durable</td><td>(only relevant for topics at present) specifies that a * durable subscription is required</td></tr> * - * <tr valign=top><td>reliability</td><td>indicates the level of reliability that the receiver - * expects. Can be one of unreliable, at-most-once, at-least-once or - * exactly-once (the latter is not yet correctly supported).</td></tr> - * * <tr valign=top><td>filter</td><td>(only relevant for topics at present) allows bindings to * be created for the queue that match the given criteria (or list of * criteria).</td></tr> @@ -133,7 +136,7 @@ class AddressImpl; * <li>exclusive, which requests an exclusive subscription and * is only relevant for queues</li> * - * <li>x-queue-arguments, which ais only relevant for topics and + * <li>x-queue-arguments, which is only relevant for topics and * allows arguments to the queue-declare for the subscription * queue to be specified</li> * </ul> diff --git a/qpid/cpp/include/qpid/messaging/Codec.h b/qpid/cpp/include/qpid/messaging/Codec.h index bacec5c786..b9b1cc862a 100644 --- a/qpid/cpp/include/qpid/messaging/Codec.h +++ b/qpid/cpp/include/qpid/messaging/Codec.h @@ -22,7 +22,7 @@ * */ #include <string> -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" namespace qpid { namespace messaging { diff --git a/qpid/cpp/include/qpid/messaging/Connection.h b/qpid/cpp/include/qpid/messaging/Connection.h index 36392da0b2..b5eeeb2980 100644 --- a/qpid/cpp/include/qpid/messaging/Connection.h +++ b/qpid/cpp/include/qpid/messaging/Connection.h @@ -22,19 +22,14 @@ * */ #include <string> -#include "qpid/client/ClientImportExport.h" -#include "qpid/client/Handle.h" +#include "qpid/messaging/ImportExport.h" +#include "qpid/messaging/Handle.h" #include "qpid/messaging/Variant.h" namespace qpid { -namespace client { - -template <class> class PrivateImplRef; - -} - namespace messaging { +template <class> class PrivateImplRef; class ConnectionImpl; class Session; @@ -43,7 +38,7 @@ struct InvalidOptionString : public qpid::Exception InvalidOptionString(const std::string& msg); }; -class Connection : public qpid::client::Handle<ConnectionImpl> +class Connection : public qpid::messaging::Handle<ConnectionImpl> { public: QPID_CLIENT_EXTERN Connection(ConnectionImpl* impl); @@ -58,30 +53,38 @@ class Connection : public qpid::client::Handle<ConnectionImpl> * sasl-mechanism * sasl-min-ssf * sasl-max-ssf + * protocol + * urls * - * (note also bounds, locale, max-channels and max-framesize, but not sure whether those should be docuemented here) + * (note also bounds, locale, max-channels and max-framesize, but + * not sure whether those should be documented here) * - * Retry behaviour can be controlled through the following options: - * - * reconnection-timeout - determines how long it will try to - * reconnect for -1 means forever, 0 - * means don't try to reconnect - * min-retry-interval - * max-retry-interval + * Reconnect behaviour can be controlled through the following options: * - * The retry-interval is the time that the client waits for - * after a failed attempt to reconnect before retrying. It + * reconnect: true/false (enables/disables reconnect entirely) + * reconnect-timeout: number of seconds (give up and report failure after specified time) + * reconnect-limit: n (give up and report failure after specified number of attempts) + * reconnect-interval-min: number of seconds (initial delay between failed reconnection attempts) + * reconnect-interval-max: number of seconds (maximum delay between failed reconnection attempts) + * reconnect-interval: shorthand for setting the same reconnect_interval_min/max + * + * The reconnect-interval is the time that the client waits + * for after a failed attempt to reconnect before retrying. It * starts at the value of the min-retry-interval and is * doubled every failure until the value of max-retry-interval * is reached. - * - * */ QPID_CLIENT_EXTERN Connection(const Variant::Map& options = Variant::Map()); QPID_CLIENT_EXTERN Connection(const std::string& options); QPID_CLIENT_EXTERN ~Connection(); QPID_CLIENT_EXTERN Connection& operator=(const Connection&); + QPID_CLIENT_EXTERN void setOption(const std::string& name, const Variant& value); QPID_CLIENT_EXTERN void open(const std::string& url); + /** + * Closes a connection and all sessions associated with it. An + * opened connection must be closed before the last handle is + * allowed to go out of scope. + */ QPID_CLIENT_EXTERN void close(); QPID_CLIENT_EXTERN Session newSession(bool transactional, const std::string& name = std::string()); QPID_CLIENT_EXTERN Session newSession(const std::string& name = std::string()); @@ -89,7 +92,7 @@ class Connection : public qpid::client::Handle<ConnectionImpl> QPID_CLIENT_EXTERN Session getSession(const std::string& name) const; private: - friend class qpid::client::PrivateImplRef<Connection>; + friend class qpid::messaging::PrivateImplRef<Connection>; }; diff --git a/qpid/cpp/include/qpid/messaging/Handle.h b/qpid/cpp/include/qpid/messaging/Handle.h new file mode 100644 index 0000000000..c528c39e19 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Handle.h @@ -0,0 +1,71 @@ +#ifndef QPID_MESSAGING_HANDLE_H +#define QPID_MESSAGING_HANDLE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/messaging/ImportExport.h" + +namespace qpid { +namespace messaging { + +template <class> class PrivateImplRef; + +/** + * A handle is like a pointer: refers to an underlying implementation object. + * Copying the handle does not copy the object. + * + * Handles can be null, like a 0 pointer. Use isValid(), isNull() or the + * conversion to bool to test for a null handle. + */ +template <class T> class Handle { + public: + + /**@return true if handle is valid, i.e. not null. */ + QPID_CLIENT_EXTERN bool isValid() const { return impl; } + + /**@return true if handle is null. It is an error to call any function on a null handle. */ + QPID_CLIENT_EXTERN bool isNull() const { return !impl; } + + /** Conversion to bool supports idiom if (handle) { handle->... } */ + QPID_CLIENT_EXTERN operator bool() const { return impl; } + + /** Operator ! supports idiom if (!handle) { do_if_handle_is_null(); } */ + QPID_CLIENT_EXTERN bool operator !() const { return !impl; } + + void swap(Handle<T>& h) { T* t = h.impl; h.impl = impl; impl = t; } + + protected: + typedef T Impl; + QPID_CLIENT_EXTERN Handle() :impl() {} + + // Not implemented,subclasses must implement. + QPID_CLIENT_EXTERN Handle(const Handle&); + QPID_CLIENT_EXTERN Handle& operator=(const Handle&); + + Impl* impl; + + friend class PrivateImplRef<T>; +}; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_HANDLE_H*/ diff --git a/qpid/cpp/include/qpid/messaging/ImportExport.h b/qpid/cpp/include/qpid/messaging/ImportExport.h new file mode 100644 index 0000000000..7113b437be --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/ImportExport.h @@ -0,0 +1,33 @@ +#ifndef QPID_MESSAGING_IMPORTEXPORT_H +#define QPID_MESSAGING_IMPORTEXPORT_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(WIN32) && !defined(QPID_DECLARE_STATIC) +#if defined(CLIENT_EXPORT) || defined (qpidclient_EXPORTS) +#define QPID_CLIENT_EXTERN __declspec(dllexport) +#else +#define QPID_CLIENT_EXTERN __declspec(dllimport) +#endif +#else +#define QPID_CLIENT_EXTERN +#endif + +#endif /*!QPID_MESSAGING_IMPORTEXPORT_H*/ diff --git a/qpid/cpp/include/qpid/messaging/ListContent.h b/qpid/cpp/include/qpid/messaging/ListContent.h index f57a920a88..3db8a8eac6 100644 --- a/qpid/cpp/include/qpid/messaging/ListContent.h +++ b/qpid/cpp/include/qpid/messaging/ListContent.h @@ -21,7 +21,7 @@ * under the License. * */ -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" #include "Variant.h" namespace qpid { diff --git a/qpid/cpp/include/qpid/messaging/ListView.h b/qpid/cpp/include/qpid/messaging/ListView.h index 4970a20072..d7c3536a9c 100644 --- a/qpid/cpp/include/qpid/messaging/ListView.h +++ b/qpid/cpp/include/qpid/messaging/ListView.h @@ -22,7 +22,7 @@ * */ -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" #include "Variant.h" namespace qpid { diff --git a/qpid/cpp/include/qpid/messaging/MapContent.h b/qpid/cpp/include/qpid/messaging/MapContent.h index 3a80a38732..78ef51e593 100644 --- a/qpid/cpp/include/qpid/messaging/MapContent.h +++ b/qpid/cpp/include/qpid/messaging/MapContent.h @@ -22,7 +22,7 @@ * */ -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" #include "Variant.h" #include <map> #include <string> diff --git a/qpid/cpp/include/qpid/messaging/MapView.h b/qpid/cpp/include/qpid/messaging/MapView.h index 910dfca5c2..baa999b4ad 100644 --- a/qpid/cpp/include/qpid/messaging/MapView.h +++ b/qpid/cpp/include/qpid/messaging/MapView.h @@ -21,7 +21,7 @@ * under the License. * */ -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" #include "Variant.h" #include <map> #include <string> diff --git a/qpid/cpp/include/qpid/messaging/Message.h b/qpid/cpp/include/qpid/messaging/Message.h index 30e15d79a3..21404d482b 100644 --- a/qpid/cpp/include/qpid/messaging/Message.h +++ b/qpid/cpp/include/qpid/messaging/Message.h @@ -25,12 +25,9 @@ #include <string> #include "qpid/messaging/Duration.h" #include "qpid/messaging/Variant.h" -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" namespace qpid { -namespace client { -} - namespace messaging { class Address; @@ -87,6 +84,7 @@ class Message QPID_CLIENT_EXTERN const std::string& getContent() const; QPID_CLIENT_EXTERN std::string& getContent(); + QPID_CLIENT_EXTERN void setContent(const std::string&); QPID_CLIENT_EXTERN void setContent(const char* chars, size_t count); QPID_CLIENT_EXTERN void getContent(std::pair<const char*, size_t>& content) const; diff --git a/qpid/cpp/include/qpid/messaging/Receiver.h b/qpid/cpp/include/qpid/messaging/Receiver.h index bc1f39bfc1..80b58106d7 100644 --- a/qpid/cpp/include/qpid/messaging/Receiver.h +++ b/qpid/cpp/include/qpid/messaging/Receiver.h @@ -22,19 +22,15 @@ * */ #include "qpid/Exception.h" -#include "qpid/client/ClientImportExport.h" -#include "qpid/client/Handle.h" +#include "qpid/messaging/ImportExport.h" +#include "qpid/messaging/Handle.h" #include "qpid/messaging/Duration.h" namespace qpid { -namespace client { +namespace messaging { template <class> class PrivateImplRef; -} - -namespace messaging { - class Message; class ReceiverImpl; class Session; @@ -42,7 +38,7 @@ class Session; /** * Interface through which messages are received. */ -class Receiver : public qpid::client::Handle<ReceiverImpl> +class Receiver : public qpid::messaging::Handle<ReceiverImpl> { public: struct NoMessageAvailable : qpid::Exception {}; @@ -60,14 +56,16 @@ class Receiver : public qpid::client::Handle<ReceiverImpl> QPID_CLIENT_EXTERN bool get(Message& message, Duration timeout=INFINITE_DURATION); /** * Retrieves a message from this receivers local queue, or waits - * for upto the specified timeout for a message to become - * available. Throws NoMessageAvailable if there is no - * message to give after waiting for the specified timeout. + * for up to the specified timeout for a message to become + * available. + * + *@exception NoMessageAvailable if there is no message to give + * after waiting for the specified timeout. */ QPID_CLIENT_EXTERN Message get(Duration timeout=INFINITE_DURATION); /** * Retrieves a message for this receivers subscription or waits - * for upto the specified timeout for one to become + * for up to the specified timeout for one to become * available. Unlike get() this method will check with the server * that there is no message for the subscription this receiver is * serving before returning false. @@ -79,6 +77,9 @@ class Receiver : public qpid::client::Handle<ReceiverImpl> * available. Unlike get() this method will check with the server * that there is no message for the subscription this receiver is * serving before throwing an exception. + * + *@exception NoMessageAvailable if there is no message to give + * after waiting for the specified timeout. */ QPID_CLIENT_EXTERN Message fetch(Duration timeout=INFINITE_DURATION); /** @@ -123,7 +124,7 @@ class Receiver : public qpid::client::Handle<ReceiverImpl> QPID_CLIENT_EXTERN Session getSession() const; private: - friend class qpid::client::PrivateImplRef<Receiver>; + friend class qpid::messaging::PrivateImplRef<Receiver>; }; }} // namespace qpid::messaging diff --git a/qpid/cpp/include/qpid/messaging/Sender.h b/qpid/cpp/include/qpid/messaging/Sender.h index eb8ccc2f84..b7dd015a55 100644 --- a/qpid/cpp/include/qpid/messaging/Sender.h +++ b/qpid/cpp/include/qpid/messaging/Sender.h @@ -21,27 +21,22 @@ * under the License. * */ -#include "qpid/client/ClientImportExport.h" -#include "qpid/client/Handle.h" +#include "qpid/messaging/ImportExport.h" +#include "qpid/messaging/Handle.h" #include "qpid/sys/IntegerTypes.h" #include <string> namespace qpid { -namespace client { - -template <class> class PrivateImplRef; - -} - namespace messaging { +template <class> class PrivateImplRef; class Message; class SenderImpl; class Session; /** * Interface through which messages are sent. */ -class Sender : public qpid::client::Handle<SenderImpl> +class Sender : public qpid::messaging::Handle<SenderImpl> { public: QPID_CLIENT_EXTERN Sender(SenderImpl* impl = 0); @@ -79,7 +74,7 @@ class Sender : public qpid::client::Handle<SenderImpl> */ QPID_CLIENT_EXTERN Session getSession() const; private: - friend class qpid::client::PrivateImplRef<Sender>; + friend class qpid::messaging::PrivateImplRef<Sender>; }; }} // namespace qpid::messaging diff --git a/qpid/cpp/include/qpid/messaging/Session.h b/qpid/cpp/include/qpid/messaging/Session.h index 87f69f268a..032b678c5c 100644 --- a/qpid/cpp/include/qpid/messaging/Session.h +++ b/qpid/cpp/include/qpid/messaging/Session.h @@ -23,20 +23,15 @@ */ #include "qpid/Exception.h" #include "qpid/messaging/Duration.h" -#include "qpid/client/ClientImportExport.h" -#include "qpid/client/Handle.h" +#include "qpid/messaging/ImportExport.h" +#include "qpid/messaging/Handle.h" #include "qpid/sys/Time.h" #include <string> namespace qpid { -namespace client { - -template <class> class PrivateImplRef; - -} - namespace messaging { +template <class> class PrivateImplRef; class Address; class Connection; class Message; @@ -55,7 +50,7 @@ struct KeyError : qpid::Exception * A session represents a distinct 'conversation' which can involve * sending and receiving messages to and from different addresses. */ -class Session : public qpid::client::Handle<SessionImpl> +class Session : public qpid::messaging::Handle<SessionImpl> { public: QPID_CLIENT_EXTERN Session(SessionImpl* impl = 0); @@ -63,6 +58,12 @@ class Session : public qpid::client::Handle<SessionImpl> QPID_CLIENT_EXTERN ~Session(); QPID_CLIENT_EXTERN Session& operator=(const Session&); + /** + * Closes a session and all associated senders and receivers. An + * opened session should be closed before the last handle to it + * goes out of scope. All a connections sessions can be closed by + * a call to Connection::close(). + */ QPID_CLIENT_EXTERN void close(); QPID_CLIENT_EXTERN void commit(); @@ -83,8 +84,8 @@ class Session : public qpid::client::Handle<SessionImpl> QPID_CLIENT_EXTERN void flush(); /** - * Returns the number of messages received and waiting to be - * fetched. + * Returns the total number of messages received and waiting to be + * fetched by all Receivers belonging to this session. */ QPID_CLIENT_EXTERN uint32_t available(); /** @@ -99,14 +100,15 @@ class Session : public qpid::client::Handle<SessionImpl> * to the specified timeout waiting for one to arrive. Returns * true if a message was available at the point of return, in * which case the passed in receiver reference will be set to the - * receiver for that message or fals if no message was available. + * receiver for that message or false if no message was available. */ QPID_CLIENT_EXTERN bool nextReceiver(Receiver&, Duration timeout=INFINITE_DURATION); /** * Returns the receiver for the next available message. If there * are no available messages at present the call will block for up - * to the specified timeout waiting for one to arrive. Will throw - * Receiver::NoMessageAvailable if no message became available in + * to the specified timeout waiting for one to arrive. + * + *@exception Receiver::NoMessageAvailable if no message became available in * time. */ QPID_CLIENT_EXTERN Receiver nextReceiver(Duration timeout=INFINITE_DURATION); @@ -126,13 +128,13 @@ class Session : public qpid::client::Handle<SessionImpl> QPID_CLIENT_EXTERN Receiver createReceiver(const std::string& address); /** - * Returns the sender with the specified name or throws KeyError - * if there is none for that name. + * Returns the sender with the specified name. + *@exception KeyError if there is none for that name. */ QPID_CLIENT_EXTERN Sender getSender(const std::string& name) const; /** - * Returns the receiver with the specified name or throws KeyError - * if there is none for that name. + * Returns the receiver with the specified name. + *@exception KeyError if there is none for that name. */ QPID_CLIENT_EXTERN Receiver getReceiver(const std::string& name) const; /** @@ -142,7 +144,7 @@ class Session : public qpid::client::Handle<SessionImpl> QPID_CLIENT_EXTERN Connection getConnection() const; private: - friend class qpid::client::PrivateImplRef<Session>; + friend class qpid::messaging::PrivateImplRef<Session>; }; }} // namespace qpid::messaging diff --git a/qpid/cpp/include/qpid/messaging/Uuid.h b/qpid/cpp/include/qpid/messaging/Uuid.h index bbc9bd7a97..d83f8495b7 100644 --- a/qpid/cpp/include/qpid/messaging/Uuid.h +++ b/qpid/cpp/include/qpid/messaging/Uuid.h @@ -22,7 +22,7 @@ * */ -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" #include <iosfwd> #include <string> diff --git a/qpid/cpp/include/qpid/messaging/Variant.h b/qpid/cpp/include/qpid/messaging/Variant.h index 0bf62a9909..51c6bd98fe 100644 --- a/qpid/cpp/include/qpid/messaging/Variant.h +++ b/qpid/cpp/include/qpid/messaging/Variant.h @@ -28,7 +28,7 @@ #include "Uuid.h" #include "qpid/Exception.h" #include "qpid/sys/IntegerTypes.h" -#include "qpid/client/ClientImportExport.h" +#include "qpid/messaging/ImportExport.h" namespace qpid { namespace messaging { diff --git a/qpid/cpp/managementgen/qmfgen/schema.py b/qpid/cpp/managementgen/qmfgen/schema.py index 5ce57bc268..ca6628c366 100755 --- a/qpid/cpp/managementgen/qmfgen/schema.py +++ b/qpid/cpp/managementgen/qmfgen/schema.py @@ -1315,13 +1315,14 @@ class SchemaClass: def genPrimaryKey (self, stream, variables): first = 1 for prop in self.properties: - if prop.isIndex == 1: - if first: - first = None - else: - stream.write(" << \",\";\n") - var = prop.type.type.stream.replace("#", prop.getName()) - stream.write(" key << %s" % var) + if prop.getName() != "vhostRef": # Limit how deep the v2Key strings get + if prop.isIndex == 1: + if first: + first = None + else: + stream.write(" << \",\";\n") + var = prop.type.type.stream.replace("#", prop.getName()) + stream.write(" key << %s" % var) if not first: stream.write(";") diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index a7078bea47..b09309cb9c 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -589,19 +589,6 @@ set_target_properties (qpidcommon PROPERTIES install (TARGETS qpidcommon DESTINATION ${QPID_INSTALL_LIBDIR} COMPONENT ${QPID_COMPONENT_COMMON}) -# When the client programming component is installed, Windows also needs -# the debug variant of qpidcommon and qpidclient, and the PDBs for those -# as well. It would be nice to figure out a way to put some sanity checking -# here... as it is, success relies on the packager building the debug then -# the release, then packaging the release build. -if (WIN32) - install (PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpidcommond.dll - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpidcommond.lib - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpidcommond.pdb - DESTINATION ${QPID_INSTALL_LIBDIR} - COMPONENT ${QPID_COMPONENT_CLIENT}) -endif (WIN32) set (qpidclient_SOURCES ${rgen_client_srcs} @@ -667,6 +654,7 @@ set (qpidclient_SOURCES qpid/client/amqp0_10/CodecsInternal.h qpid/client/amqp0_10/ConnectionImpl.h qpid/client/amqp0_10/ConnectionImpl.cpp + qpid/client/amqp0_10/FailoverUpdates.cpp qpid/client/amqp0_10/IncomingMessages.h qpid/client/amqp0_10/IncomingMessages.cpp qpid/client/amqp0_10/MessageSink.h @@ -679,6 +667,8 @@ set (qpidclient_SOURCES qpid/client/amqp0_10/SessionImpl.cpp qpid/client/amqp0_10/SenderImpl.h qpid/client/amqp0_10/SenderImpl.cpp + qpid/client/amqp0_10/SimpleUrlParser.h + qpid/client/amqp0_10/SimpleUrlParser.cpp ) add_library (qpidclient SHARED ${qpidclient_SOURCES}) @@ -699,14 +689,6 @@ if (NOT QPID_GENERATED_HEADERS_IN_SOURCE) DESTINATION ${QPID_INSTALL_INCLUDEDIR} COMPONENT ${QPID_COMPONENT_CLIENT_INCLUDE}) endif (NOT QPID_GENERATED_HEADERS_IN_SOURCE) -if (WIN32) - install (PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpidclientd.dll - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpidclientd.lib - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpidclientd.pdb - DESTINATION ${QPID_INSTALL_LIBDIR} - COMPONENT ${QPID_COMPONENT_CLIENT}) -endif (WIN32) if (WIN32) set(AMQP_WCF_DIR ${qpid-cpp_SOURCE_DIR}/../wcf) @@ -784,7 +766,8 @@ set (qpidbroker_SOURCES qpid/broker/TxPublish.cpp qpid/broker/Vhost.cpp qpid/management/ManagementAgent.cpp - qpid/management/ManagementExchange.cpp + qpid/management/ManagementDirectExchange.cpp + qpid/management/ManagementTopicExchange.cpp qpid/sys/TCPIOPlugin.cpp ) add_library (qpidbroker SHARED ${qpidbroker_SOURCES}) @@ -828,7 +811,7 @@ set (qmf_SOURCES qpid/agent/ManagementAgentImpl.h ) add_library (qmf SHARED ${qmf_SOURCES}) -target_link_libraries (qmf qmfengine) +target_link_libraries (qmf qpidclient) set_target_properties (qmf PROPERTIES VERSION ${qmf_version}) install (TARGETS qmf OPTIONAL @@ -922,15 +905,6 @@ set_target_properties (qmfconsole PROPERTIES install (TARGETS qmfconsole DESTINATION ${QPID_INSTALL_LIBDIR} COMPONENT ${QPID_COMPONENT_QMF}) -# On Windows, also grab the debug version of qmfconsole. -if (WIN32) - install (PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qmfconsoled.dll - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qmfconsoled.lib - ${CMAKE_CURRENT_BINARY_DIR}/Debug/qmfconsoled.pdb - DESTINATION ${QPID_INSTALL_LIBDIR} - COMPONENT ${QPID_COMPONENT_QMF}) -endif (WIN32) # A queue event listener plugin that creates messages on a replication # queue corresponding to enqueue and dequeue events: @@ -976,6 +950,5 @@ add_definitions(-DHAVE_CONFIG_H) # Now create the config file from all the info learned above. configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) - add_subdirectory(qpid/store) add_subdirectory(tests) diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 3a3f23056a..c66263b42a 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -465,6 +465,7 @@ libqpidcommon_la_SOURCES += \ qpid/sys/Runnable.cpp \ qpid/sys/ScopedIncrement.h \ qpid/sys/SecurityLayer.h \ + qpid/sys/SecuritySettings.h \ qpid/sys/Semaphore.h \ qpid/sys/Shlib.cpp \ qpid/sys/Shlib.h \ @@ -637,8 +638,10 @@ libqpidbroker_la_SOURCES = \ qpid/management/IdAllocator.h \ qpid/management/ManagementAgent.cpp \ qpid/management/ManagementAgent.h \ - qpid/management/ManagementExchange.cpp \ - qpid/management/ManagementExchange.h \ + qpid/management/ManagementDirectExchange.cpp \ + qpid/management/ManagementDirectExchange.h \ + qpid/management/ManagementTopicExchange.cpp \ + qpid/management/ManagementTopicExchange.h \ qpid/sys/TCPIOPlugin.cpp @@ -711,6 +714,7 @@ libqpidclient_la_SOURCES = \ qpid/messaging/Message.cpp \ qpid/messaging/MessageImpl.h \ qpid/messaging/MessageImpl.cpp \ + qpid/messaging/PrivateImplRef.h \ qpid/messaging/Sender.cpp \ qpid/messaging/Receiver.cpp \ qpid/messaging/Session.cpp \ @@ -728,6 +732,7 @@ libqpidclient_la_SOURCES = \ qpid/client/amqp0_10/CodecsInternal.h \ qpid/client/amqp0_10/ConnectionImpl.h \ qpid/client/amqp0_10/ConnectionImpl.cpp \ + qpid/client/amqp0_10/FailoverUpdates.cpp \ qpid/client/amqp0_10/IncomingMessages.h \ qpid/client/amqp0_10/IncomingMessages.cpp \ qpid/client/amqp0_10/MessageSink.h \ @@ -739,7 +744,9 @@ libqpidclient_la_SOURCES = \ qpid/client/amqp0_10/SessionImpl.h \ qpid/client/amqp0_10/SessionImpl.cpp \ qpid/client/amqp0_10/SenderImpl.h \ - qpid/client/amqp0_10/SenderImpl.cpp + qpid/client/amqp0_10/SenderImpl.cpp \ + qpid/client/amqp0_10/SimpleUrlParser.h \ + qpid/client/amqp0_10/SimpleUrlParser.cpp # NOTE: only public header files (which should be in ../include) # should go in this list. Private headers should go in the SOURCES @@ -812,20 +819,23 @@ nobase_include_HEADERS += \ ../include/qpid/sys/Thread.h \ ../include/qpid/sys/Time.h \ ../include/qpid/messaging/Address.h \ - ../include/qpid/messaging/Connection.h \ ../include/qpid/messaging/Codec.h \ + ../include/qpid/messaging/Connection.h \ ../include/qpid/messaging/Duration.h \ + ../include/qpid/messaging/Handle.h \ + ../include/qpid/messaging/ImportExport.h \ ../include/qpid/messaging/ListContent.h \ ../include/qpid/messaging/ListView.h \ ../include/qpid/messaging/MapContent.h \ ../include/qpid/messaging/MapView.h \ ../include/qpid/messaging/Message.h \ - ../include/qpid/messaging/Sender.h \ ../include/qpid/messaging/Receiver.h \ + ../include/qpid/messaging/Sender.h \ ../include/qpid/messaging/Session.h \ ../include/qpid/messaging/Uuid.h \ ../include/qpid/messaging/Variant.h \ - ../include/qpid/client/amqp0_10/Codecs.h + ../include/qpid/client/amqp0_10/Codecs.h \ + ../include/qpid/client/amqp0_10/FailoverUpdates.h # Force build of qpidd during dist phase so help2man will work. dist-hook: $(BUILT_SOURCES) diff --git a/qpid/cpp/src/qpid/Version.h b/qpid/cpp/src/qpid/Version.h index c8add11112..b4805a3757 100755 --- a/qpid/cpp/src/qpid/Version.h +++ b/qpid/cpp/src/qpid/Version.h @@ -23,10 +23,11 @@ #ifdef HAVE_CONFIG_H # include "config.h" +#else +# error "config.h not generated" #endif namespace qpid { -#ifdef HAVE_CONFIG_H const std::string product = PACKAGE_NAME; const std::string version = PACKAGE_VERSION; # if HAVE_SASL @@ -34,11 +35,6 @@ namespace qpid { # else const std::string saslName = "qpidd-no-sasl"; # endif -#else - const std::string product = "qpidc"; - const std::string version = "0.7"; - const std::string saslName = "qpid-broker"; -#endif } #endif /*!QPID_VERSION_H*/ diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp index cbccca6eea..d94f228734 100644 --- a/qpid/cpp/src/qpid/broker/Broker.cpp +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -35,7 +35,8 @@ #include "qmf/org/apache/qpid/broker/Package.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerEcho.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h" -#include "qpid/management/ManagementExchange.h" +#include "qpid/management/ManagementDirectExchange.h" +#include "qpid/management/ManagementTopicExchange.h" #include "qpid/log/Statement.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/ProtocolInitiation.h" @@ -234,11 +235,22 @@ Broker::Broker(const Broker::Options& conf) : declareStandardExchange(amq_match, HeadersExchange::typeName); if(conf.enableMgmt) { - exchanges.declare(qpid_management, ManagementExchange::typeName); - Exchange::shared_ptr mExchange = exchanges.get (qpid_management); - Exchange::shared_ptr dExchange = exchanges.get (amq_direct); + exchanges.declare(qpid_management, ManagementTopicExchange::typeName); + Exchange::shared_ptr mExchange = exchanges.get(qpid_management); + Exchange::shared_ptr dExchange = exchanges.get(amq_direct); managementAgent->setExchange(mExchange, dExchange); - boost::dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent(managementAgent.get()); + boost::dynamic_pointer_cast<ManagementTopicExchange>(mExchange)->setManagmentAgent(managementAgent.get(), 1); + + std::string qmfTopic("qmf.default.topic"); + std::string qmfDirect("qmf.default.direct"); + + std::pair<Exchange::shared_ptr, bool> topicPair(exchanges.declare(qmfTopic, ManagementTopicExchange::typeName)); + std::pair<Exchange::shared_ptr, bool> directPair(exchanges.declare(qmfDirect, ManagementDirectExchange::typeName)); + + boost::dynamic_pointer_cast<ManagementDirectExchange>(directPair.first)->setManagmentAgent(managementAgent.get(), 2); + boost::dynamic_pointer_cast<ManagementTopicExchange>(topicPair.first)->setManagmentAgent(managementAgent.get(), 2); + + managementAgent->setExchangeV2(topicPair.first, directPair.first); } else QPID_LOG(info, "Management not enabled"); diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp index 532666ad76..2bb68b9f2d 100644 --- a/qpid/cpp/src/qpid/broker/Connection.cpp +++ b/qpid/cpp/src/qpid/broker/Connection.cpp @@ -23,6 +23,7 @@ #include "qpid/broker/SessionState.h" #include "qpid/broker/Bridge.h" #include "qpid/broker/Broker.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/log/Statement.h" #include "qpid/ptr_map.h" @@ -72,9 +73,10 @@ struct ConnectionTimeoutTask : public sys::TimerTask { } }; -Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, unsigned int ssf, bool isLink_, uint64_t objectId, bool shadow_) : +Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, + const qpid::sys::SecuritySettings& external, bool isLink_, uint64_t objectId, bool shadow_) : ConnectionState(out_, broker_), - ssf(ssf), + securitySettings(external), adapter(*this, isLink_), isLink(isLink_), mgmtClosing(false), @@ -99,7 +101,7 @@ Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std if (agent != 0) { mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !isLink, false); mgmtObject->set_shadow(shadow); - agent->addObject(mgmtObject, objectId, true); + agent->addObject(mgmtObject, objectId); } ConnectionState::setUrl(mgmtId); } diff --git a/qpid/cpp/src/qpid/broker/Connection.h b/qpid/cpp/src/qpid/broker/Connection.h index d49d9f4d75..30a763411f 100644 --- a/qpid/cpp/src/qpid/broker/Connection.h +++ b/qpid/cpp/src/qpid/broker/Connection.h @@ -45,6 +45,7 @@ #include "qpid/sys/AggregateOutput.h" #include "qpid/sys/ConnectionInputHandler.h" #include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/sys/Socket.h" #include "qpid/sys/TimeoutHandler.h" #include "qpid/sys/Mutex.h" @@ -78,7 +79,8 @@ class Connection : public sys::ConnectionInputHandler, virtual void connectionError(const std::string&) = 0; }; - Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, unsigned int ssf, + Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, + const qpid::sys::SecuritySettings&, bool isLink = false, uint64_t objectId = 0, bool shadow=false); ~Connection (); @@ -136,14 +138,17 @@ class Connection : public sys::ConnectionInputHandler, // Used by cluster to update connection status sys::AggregateOutput& getOutputTasks() { return outputTasks; } - unsigned int getSSF() { return ssf; } + const qpid::sys::SecuritySettings& getExternalSecuritySettings() const + { + return securitySettings; + } private: typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap; typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; ChannelMap channels; - unsigned int ssf; + qpid::sys::SecuritySettings securitySettings; ConnectionHandler adapter; const bool isLink; bool mgmtClosing; diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp index ffb0b34b95..9e0020812b 100644 --- a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp +++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp @@ -22,12 +22,14 @@ #include "qpid/framing/ProtocolVersion.h" #include "qpid/amqp_0_10/Connection.h" #include "qpid/broker/Connection.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/log/Statement.h" namespace qpid { namespace broker { using framing::ProtocolVersion; +using qpid::sys::SecuritySettings; typedef std::auto_ptr<amqp_0_10::Connection> ConnectionPtr; typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr; @@ -37,7 +39,7 @@ ConnectionFactory::~ConnectionFactory() {} sys::ConnectionCodec* ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, - unsigned int ) { + const SecuritySettings& external) { if (broker.getConnectionCounter().allowConnection()) { QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused"); @@ -45,7 +47,7 @@ ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std: } if (v == ProtocolVersion(0, 10)) { ConnectionPtr c(new amqp_0_10::Connection(out, id, false)); - c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, false))); + c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, false))); return c.release(); } return 0; @@ -53,10 +55,10 @@ ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std: sys::ConnectionCodec* ConnectionFactory::create(sys::OutputControl& out, const std::string& id, - unsigned int) { + const SecuritySettings& external) { // used to create connections from one broker to another ConnectionPtr c(new amqp_0_10::Connection(out, id, true)); - c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, true))); + c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, true))); return c.release(); } diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.h b/qpid/cpp/src/qpid/broker/ConnectionFactory.h index d812292ad1..7c1a9a08e1 100644 --- a/qpid/cpp/src/qpid/broker/ConnectionFactory.h +++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.h @@ -36,11 +36,10 @@ class ConnectionFactory : public sys::ConnectionCodec::Factory sys::ConnectionCodec* create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, - unsigned int conn_ssf); + const qpid::sys::SecuritySettings&); sys::ConnectionCodec* - create(sys::OutputControl&, const std::string& id, - unsigned int conn_ssf); + create(sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&); private: Broker& broker; diff --git a/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp index f4a860fa1e..20fdc4164a 100644 --- a/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp +++ b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -24,7 +24,8 @@ #include "qpid/broker/FanOutExchange.h" #include "qpid/broker/HeadersExchange.h" #include "qpid/broker/TopicExchange.h" -#include "qpid/management/ManagementExchange.h" +#include "qpid/management/ManagementDirectExchange.h" +#include "qpid/management/ManagementTopicExchange.h" #include "qpid/framing/reply_exceptions.h" using namespace qpid::broker; @@ -52,8 +53,10 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent, broker)); }else if (type == HeadersExchange::typeName) { exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent, broker)); - }else if (type == ManagementExchange::typeName) { - exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent, broker)); + }else if (type == ManagementDirectExchange::typeName) { + exchange = Exchange::shared_ptr(new ManagementDirectExchange(name, durable, args, parent, broker)); + }else if (type == ManagementTopicExchange::typeName) { + exchange = Exchange::shared_ptr(new ManagementTopicExchange(name, durable, args, parent, broker)); }else{ FunctionMap::iterator i = factory.find(type); if (i == factory.end()) { diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp index 640036e741..daf53085bf 100644 --- a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp @@ -252,7 +252,7 @@ namespace { bool match_values(const FieldValue& bind, const FieldValue& msg) { - return bind.empty() || bind == msg; + return bind.getType() == 0xf0 || bind == msg; } } diff --git a/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp b/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp index 02265ab85c..34d92fa752 100644 --- a/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp +++ b/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp @@ -30,7 +30,8 @@ IncompleteMessageList::IncompleteMessageList() : IncompleteMessageList::~IncompleteMessageList() { - sys::Mutex::ScopedLock l(lock); + // No lock here. We are relying on Messsag::reset*CompleteCallback + // to ensure no callbacks are in progress before they return. for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ++i) { (*i)->resetEnqueueCompleteCallback(); (*i)->resetDequeueCompleteCallback(); @@ -78,7 +79,7 @@ void IncompleteMessageList::each(const CompletionListener& listen) { sys::Mutex::ScopedLock l(lock); snapshot = incomplete; } - std::for_each(incomplete.begin(), incomplete.end(), listen); // FIXME aconway 2008-11-07: passed by ref or value? + std::for_each(incomplete.begin(), incomplete.end(), listen); } }} diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp index ff4a37c88f..329451d64e 100644 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -49,7 +49,8 @@ TransferAdapter Message::TRANSFER; Message::Message(const framing::SequenceNumber& id) : frames(id), persistenceId(0), redelivered(false), loaded(false), staged(false), forcePersistentPolicy(false), publisher(0), adapter(0), - expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0), requiredCredit(0) {} + expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0), + inCallback(false), requiredCredit(0) {} Message::~Message() { @@ -398,35 +399,55 @@ void Message::setReplacementMessage(boost::intrusive_ptr<Message> msg, const Que replacement[qfor] = msg; } +namespace { +struct ScopedSet { + sys::Monitor& lock; + bool& flag; + ScopedSet(sys::Monitor& l, bool& f) : lock(l), flag(f) { + sys::Monitor::ScopedLock sl(lock); + flag = true; + } + ~ScopedSet(){ + sys::Monitor::ScopedLock sl(lock); + flag = false; + lock.notifyAll(); + } +}; +} + void Message::allEnqueuesComplete() { - sys::Mutex::ScopedLock l(callbackLock); + ScopedSet ss(callbackLock, inCallback); MessageCallback* cb = enqueueCallback; if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); } void Message::allDequeuesComplete() { - sys::Mutex::ScopedLock l(callbackLock); + ScopedSet ss(callbackLock, inCallback); MessageCallback* cb = dequeueCallback; if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); } void Message::setEnqueueCompleteCallback(MessageCallback& cb) { sys::Mutex::ScopedLock l(callbackLock); + while (inCallback) callbackLock.wait(); enqueueCallback = &cb; } void Message::resetEnqueueCompleteCallback() { sys::Mutex::ScopedLock l(callbackLock); + while (inCallback) callbackLock.wait(); enqueueCallback = 0; } void Message::setDequeueCompleteCallback(MessageCallback& cb) { sys::Mutex::ScopedLock l(callbackLock); + while (inCallback) callbackLock.wait(); dequeueCallback = &cb; } void Message::resetDequeueCompleteCallback() { sys::Mutex::ScopedLock l(callbackLock); + while (inCallback) callbackLock.wait(); dequeueCallback = 0; } diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h index 0a7772040b..353044c577 100644 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -26,7 +26,7 @@ #include "qpid/broker/PersistableMessage.h" #include "qpid/broker/MessageAdapter.h" #include "qpid/framing/amqp_types.h" -#include "qpid/sys/Mutex.h" +#include "qpid/sys/Monitor.h" #include "qpid/sys/Time.h" #include <boost/function.hpp> #include <boost/shared_ptr.hpp> @@ -189,9 +189,10 @@ public: mutable Replacement replacement; mutable boost::intrusive_ptr<Message> empty; - sys::Mutex callbackLock; + sys::Monitor callbackLock; MessageCallback* enqueueCallback; MessageCallback* dequeueCallback; + bool inCallback; uint32_t requiredCredit; }; diff --git a/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp index 5611e3ec06..0f72f9643d 100644 --- a/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp +++ b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp @@ -26,6 +26,7 @@ #include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/SecuritySettings.h" #include <boost/format.hpp> #if HAVE_SASL @@ -36,6 +37,7 @@ using qpid::sys::cyrus::CyrusSecurityLayer; using namespace qpid::framing; using qpid::sys::SecurityLayer; +using qpid::sys::SecuritySettings; using boost::format; using boost::str; @@ -152,7 +154,8 @@ void NullAuthenticator::start(const string& mechanism, const string& response) #if HAVE_SASL // encryption required - check to see if we are running over an // encrypted SSL connection. - sasl_ssf_t external_ssf = (sasl_ssf_t) connection.getSSF(); + SecuritySettings external = connection.getExternalSecuritySettings(); + sasl_ssf_t external_ssf = (sasl_ssf_t) external.ssf; if (external_ssf < 1) // < 1 == unencrypted #endif { @@ -244,7 +247,9 @@ void CyrusAuthenticator::init() // If the transport provides encryption, notify the SASL library of // the key length and set the ssf range to prevent double encryption. - sasl_ssf_t external_ssf = (sasl_ssf_t) connection.getSSF(); + SecuritySettings external = connection.getExternalSecuritySettings(); + QPID_LOG(debug, "External ssf=" << external.ssf << " and auth=" << external.authid); + sasl_ssf_t external_ssf = (sasl_ssf_t) external.ssf; if (external_ssf) { int result = sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, &external_ssf); if (result != SASL_OK) { @@ -258,16 +263,25 @@ void CyrusAuthenticator::init() ", max_ssf: " << secprops.max_ssf << ", external_ssf: " << external_ssf ); + if (!external.authid.empty()) { + const char* external_authid = external.authid.c_str(); + int result = sasl_setprop(sasl_conn, SASL_AUTH_EXTERNAL, external_authid); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result)); + } + + QPID_LOG(debug, "external auth detected and set to " << external_authid); + } + secprops.maxbufsize = 65535; secprops.property_names = 0; secprops.property_values = 0; secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */ - + if (external.nodict) secprops.security_flags |= SASL_SEC_NODICTIONARY; int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops); if (result != SASL_OK) { throw framing::InternalErrorException(QPID_MSG("SASL error: " << result)); } - } CyrusAuthenticator::~CyrusAuthenticator() diff --git a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp index 5a31dbceeb..754b443c22 100644 --- a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp +++ b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp @@ -23,12 +23,14 @@ #include "qpid/amqp_0_10/Connection.h" #include "qpid/broker/Connection.h" #include "qpid/broker/SecureConnection.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/log/Statement.h" namespace qpid { namespace broker { using framing::ProtocolVersion; +using qpid::sys::SecuritySettings; typedef std::auto_ptr<amqp_0_10::Connection> CodecPtr; typedef std::auto_ptr<SecureConnection> SecureConnectionPtr; typedef std::auto_ptr<Connection> ConnectionPtr; @@ -38,7 +40,7 @@ SecureConnectionFactory::SecureConnectionFactory(Broker& b) : broker(b) {} sys::ConnectionCodec* SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, - unsigned int conn_ssf ) { + const SecuritySettings& external) { if (broker.getConnectionCounter().allowConnection()) { QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused"); @@ -47,7 +49,7 @@ SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, cons if (v == ProtocolVersion(0, 10)) { SecureConnectionPtr sc(new SecureConnection()); CodecPtr c(new amqp_0_10::Connection(out, id, false)); - ConnectionPtr i(new broker::Connection(c.get(), broker, id, conn_ssf, false)); + ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, false)); i->setSecureConnection(sc.get()); c->setInputHandler(InputPtr(i.release())); sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c)); @@ -58,11 +60,11 @@ SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, cons sys::ConnectionCodec* SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id, - unsigned int conn_ssf) { + const SecuritySettings& external) { // used to create connections from one broker to another SecureConnectionPtr sc(new SecureConnection()); CodecPtr c(new amqp_0_10::Connection(out, id, true)); - ConnectionPtr i(new broker::Connection(c.get(), broker, id, conn_ssf, true )); + ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, true )); i->setSecureConnection(sc.get()); c->setInputHandler(InputPtr(i.release())); sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c)); diff --git a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h index b1af6d4a0f..8a04dfcb15 100644 --- a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h +++ b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.h @@ -34,11 +34,10 @@ class SecureConnectionFactory : public sys::ConnectionCodec::Factory sys::ConnectionCodec* create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, - unsigned int conn_ssf); + const qpid::sys::SecuritySettings&); sys::ConnectionCodec* - create(sys::OutputControl&, const std::string& id, - unsigned int conn_ssf); + create(sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&); private: Broker& broker; diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp index 8f1cc7b03f..9d68448d9d 100644 --- a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp @@ -213,7 +213,7 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me if (sasl.get()) { string response = sasl->start(mechanism.empty() ? mechlist : mechanism, - getSSF ? getSSF() : 0); + 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/ConnectionHandler.h b/qpid/cpp/src/qpid/client/ConnectionHandler.h index ed1e385dcf..5f4b454f53 100644 --- a/qpid/cpp/src/qpid/client/ConnectionHandler.h +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.h @@ -40,6 +40,11 @@ #include <memory> namespace qpid { + +namespace sys { +struct SecuritySettings; +} + namespace client { class ConnectionHandler : private StateManager, @@ -95,7 +100,7 @@ public: using InputHandler::handle; typedef boost::function<void()> CloseListener; typedef boost::function<void(uint16_t, const std::string&)> ErrorListener; - typedef boost::function<unsigned int()> GetConnSSF; + typedef boost::function<const qpid::sys::SecuritySettings*()> GetSecuritySettings; ConnectionHandler(const ConnectionSettings&, framing::ProtocolVersion&); @@ -123,7 +128,7 @@ public: static framing::connection::CloseCode convert(uint16_t replyCode); const std::string& getUserId() const { return operUserId; } - GetConnSSF getSSF; /** query the connection for its security strength factor */ + GetSecuritySettings getSecuritySettings; /** query the transport for its security details */ }; }} diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp index 80cd510886..ec1f4584db 100644 --- a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp +++ b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp @@ -134,6 +134,15 @@ IOThread& theIO() { return io; } +// Bring theIO into existence on library load rather than first use. +// This avoids it being destroyed whilst something in the main program +// still exists +struct InitAtLoad { + InitAtLoad() { + (void) theIO(); + } +} init; + class HeartbeatTask : public TimerTask { TimeoutHandler& timeout; @@ -165,7 +174,7 @@ ConnectionImpl::ConnectionImpl(framing::ProtocolVersion v, const ConnectionSetti CLOSE_CODE_NORMAL, std::string()); //only set error handler once open handler.onError = boost::bind(&ConnectionImpl::closed, this, _1, _2); - handler.getSSF = boost::bind(&Connector::getSSF, boost::ref(connector)); + handler.getSecuritySettings = boost::bind(&Connector::getSecuritySettings, boost::ref(connector)); } const uint16_t ConnectionImpl::NEXT_CHANNEL = std::numeric_limits<uint16_t>::max(); @@ -195,7 +204,8 @@ void ConnectionImpl::addSession(const boost::shared_ptr<SessionImpl>& session, u throw SessionBusyException(QPID_MSG("Channel " << ss->getChannel() << " attached to " << ss->getId())); } //else channel is busy, but we can keep looking for a free one } - + // If we get here, we didn't find any available channel. + throw ResourceLimitExceededException("There are no channels available"); } void ConnectionImpl::handle(framing::AMQFrame& frame) diff --git a/qpid/cpp/src/qpid/client/Connector.h b/qpid/cpp/src/qpid/client/Connector.h index 0203895b00..586012f9d6 100644 --- a/qpid/cpp/src/qpid/client/Connector.h +++ b/qpid/cpp/src/qpid/client/Connector.h @@ -35,6 +35,7 @@ namespace sys { class ShutdownHandler; class SecurityLayer; class Poller; +struct SecuritySettings; } namespace framing { @@ -74,7 +75,7 @@ class Connector : public framing::OutputHandler virtual void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>); - virtual unsigned int getSSF() = 0; + virtual const qpid::sys::SecuritySettings* getSecuritySettings() = 0; }; }} diff --git a/qpid/cpp/src/qpid/client/RdmaConnector.cpp b/qpid/cpp/src/qpid/client/RdmaConnector.cpp index 2bdfb8d68f..42b4649203 100644 --- a/qpid/cpp/src/qpid/client/RdmaConnector.cpp +++ b/qpid/cpp/src/qpid/client/RdmaConnector.cpp @@ -109,7 +109,7 @@ class RdmaConnector : public Connector, public sys::Codec framing::OutputHandler* getOutputHandler(); const std::string& getIdentifier() const; void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>); - unsigned int getSSF() { return 0; } + const qpid::sys::SecuritySettings* getSecuritySettings() { return 0; } size_t decode(const char* buffer, size_t size); size_t encode(const char* buffer, size_t size); diff --git a/qpid/cpp/src/qpid/client/Sasl.h b/qpid/cpp/src/qpid/client/Sasl.h index 63da37fcb1..56735a5fc3 100644 --- a/qpid/cpp/src/qpid/client/Sasl.h +++ b/qpid/cpp/src/qpid/client/Sasl.h @@ -30,6 +30,7 @@ namespace qpid { namespace sys { class SecurityLayer; +struct SecuritySettings; } namespace client { @@ -48,17 +49,10 @@ class Sasl * * @param mechanisms Comma-separated list of the SASL mechanism the * client supports. - * @param ssf Security Strength Factor (SSF). SSF is used to negotiate - * a SASL security layer on top of the connection should both - * parties require and support it. The value indicates the - * required level of security for communication. Possible - * values are: - * @li 0 No security - * @li 1 Integrity checking only - * @li >1 Integrity and confidentiality with the number - * giving the encryption key length. + * @param externalSecuritySettings security related details from the underlying transport */ - virtual std::string start(const std::string& mechanisms, unsigned int ssf) = 0; + virtual std::string start(const std::string& mechanisms, + const qpid::sys::SecuritySettings* externalSecuritySettings = 0) = 0; virtual std::string step(const std::string& challenge) = 0; virtual std::string getMechanism() = 0; virtual std::string getUserId() = 0; diff --git a/qpid/cpp/src/qpid/client/SaslFactory.cpp b/qpid/cpp/src/qpid/client/SaslFactory.cpp index 5012b75c94..ec5680c8d8 100644 --- a/qpid/cpp/src/qpid/client/SaslFactory.cpp +++ b/qpid/cpp/src/qpid/client/SaslFactory.cpp @@ -61,6 +61,7 @@ std::auto_ptr<SaslFactory> SaslFactory::instance; #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/sys/SecurityLayer.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/sys/cyrus/CyrusSecurityLayer.h" #include "qpid/log/Statement.h" #include <sasl/sasl.h> @@ -70,6 +71,7 @@ namespace qpid { namespace client { using qpid::sys::SecurityLayer; +using qpid::sys::SecuritySettings; using qpid::sys::cyrus::CyrusSecurityLayer; using qpid::framing::InternalErrorException; @@ -80,7 +82,7 @@ class CyrusSasl : public Sasl public: CyrusSasl(const ConnectionSettings&); ~CyrusSasl(); - std::string start(const std::string& mechanisms, unsigned int ssf); + std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings); std::string step(const std::string& challenge); std::string getMechanism(); std::string getUserId(); @@ -176,7 +178,7 @@ namespace { const std::string SSL("ssl"); } -std::string CyrusSasl::start(const std::string& mechanisms, unsigned int ssf) +std::string CyrusSasl::start(const std::string& mechanisms, const SecuritySettings* externalSettings) { QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << ")"); int result = sasl_client_new(settings.service.c_str(), @@ -190,14 +192,22 @@ std::string CyrusSasl::start(const std::string& mechanisms, unsigned int ssf) sasl_security_properties_t secprops; - if (ssf) { - sasl_ssf_t external_ssf = (sasl_ssf_t) ssf; + if (externalSettings) { + sasl_ssf_t external_ssf = (sasl_ssf_t) externalSettings->ssf; if (external_ssf) { int result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &external_ssf); if (result != SASL_OK) { throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result)); } - QPID_LOG(debug, "external SSF detected and set to " << ssf); + QPID_LOG(debug, "external SSF detected and set to " << external_ssf); + } + if (externalSettings->authid.size()) { + const char* external_authid = externalSettings->authid.c_str(); + result = sasl_setprop(conn, SASL_AUTH_EXTERNAL, external_authid); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result)); + } + QPID_LOG(debug, "external auth detected and set to " << external_authid); } } @@ -216,7 +226,6 @@ std::string CyrusSasl::start(const std::string& mechanisms, unsigned int ssf) throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(conn))); } - sasl_interact_t* client_interact = 0; const char *out = 0; unsigned outlen = 0; diff --git a/qpid/cpp/src/qpid/client/SslConnector.cpp b/qpid/cpp/src/qpid/client/SslConnector.cpp index cf6d54d261..0c794145db 100644 --- a/qpid/cpp/src/qpid/client/SslConnector.cpp +++ b/qpid/cpp/src/qpid/client/SslConnector.cpp @@ -34,6 +34,7 @@ #include "qpid/sys/ssl/SslSocket.h" #include "qpid/sys/Dispatcher.h" #include "qpid/sys/Poller.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/Msg.h" #include <iostream> @@ -86,6 +87,7 @@ class SslConnector : public Connector const uint16_t maxFrameSize; framing::ProtocolVersion version; bool initiated; + SecuritySettings securitySettings; sys::Mutex closedLock; bool closed; @@ -125,7 +127,7 @@ class SslConnector : public Connector sys::ShutdownHandler* getShutdownHandler() const; framing::OutputHandler* getOutputHandler(); const std::string& getIdentifier() const; - unsigned int getSSF() { return socket.getKeyLen(); } + const SecuritySettings* getSecuritySettings(); public: SslConnector(Poller::shared_ptr p, framing::ProtocolVersion pVersion, @@ -366,4 +368,11 @@ void SslConnector::eof(SslIO&) { handleClosed(); } +const SecuritySettings* SslConnector::getSecuritySettings() +{ + securitySettings.ssf = socket.getKeyLen(); + securitySettings.authid = "dummy";//set to non-empty string to enable external authentication + return &securitySettings; +} + }} // namespace qpid::client diff --git a/qpid/cpp/src/qpid/client/TCPConnector.cpp b/qpid/cpp/src/qpid/client/TCPConnector.cpp index 78c9b32069..1a245fe2c8 100644 --- a/qpid/cpp/src/qpid/client/TCPConnector.cpp +++ b/qpid/cpp/src/qpid/client/TCPConnector.cpp @@ -199,8 +199,19 @@ void TCPConnector::send(AMQFrame& frame) { } else { notifyWrite = (currentSize >= maxFrameSize); } - } + /* + NOTE: Moving the following line into this mutex block + is a workaround for BZ 570168, in which the test + testConcurrentSenders causes a hang about 1.5% + of the time. ( To see the hang much more frequently + leave this line out of the mutex block, and put a + small usleep just before it.) + + TODO mgoulish - fix the underlying cause and then + move this call back outside the mutex. + */ if (notifyWrite && !closed) aio->notifyPendingWrite(); + } } void TCPConnector::handleClosed() { diff --git a/qpid/cpp/src/qpid/client/TCPConnector.h b/qpid/cpp/src/qpid/client/TCPConnector.h index 6ca750f52f..6d447def2e 100644 --- a/qpid/cpp/src/qpid/client/TCPConnector.h +++ b/qpid/cpp/src/qpid/client/TCPConnector.h @@ -92,7 +92,7 @@ class TCPConnector : public Connector, public sys::Codec framing::OutputHandler* getOutputHandler(); const std::string& getIdentifier() const; void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>); - unsigned int getSSF() { return 0; } + const qpid::sys::SecuritySettings* getSecuritySettings() { return 0; } size_t decode(const char* buffer, size_t size); size_t encode(const char* buffer, size_t size); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp index 835d80185a..3e17fc968b 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp @@ -25,8 +25,10 @@ #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/List.h" +#include "qpid/log/Statement.h" #include <algorithm> #include <functional> +#include <limits> using namespace qpid::framing; using namespace qpid::messaging; @@ -39,6 +41,7 @@ namespace { const std::string iso885915("iso-8859-15"); const std::string utf8("utf8"); const std::string utf16("utf16"); +const std::string binary("binary"); const std::string amqp0_10_binary("amqp0-10:binary"); const std::string amqp0_10_bit("amqp0-10:bit"); const std::string amqp0_10_datetime("amqp0-10:datetime"); @@ -129,7 +132,7 @@ Variant toVariant(boost::shared_ptr<FieldValue> in) case 0x02: out = in->getIntegerValue<int8_t, 1>(); break; case 0x03: out = in->getIntegerValue<uint8_t, 1>(); break; case 0x04: break; //TODO: iso-8859-15 char - case 0x08: out = in->getIntegerValue<bool, 1>(); break; + case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t, 1>()); break; case 0x10: out.setEncoding(amqp0_10_binary); case 0x11: out = in->getIntegerValue<int16_t, 2>(); break; case 0x12: out = in->getIntegerValue<uint16_t, 2>(); break; @@ -163,8 +166,8 @@ Variant toVariant(boost::shared_ptr<FieldValue> in) case 0x96: case 0xa0: case 0xab: - setEncodingFor(out, in->getType()); out = in->get<std::string>(); + setEncodingFor(out, in->getType()); break; case 0xa8: @@ -188,6 +191,28 @@ Variant toVariant(boost::shared_ptr<FieldValue> in) return out; } +boost::shared_ptr<FieldValue> convertString(const std::string& value, const std::string& encoding) +{ + bool large = value.size() > std::numeric_limits<uint16_t>::max(); + if (encoding.empty() || encoding == amqp0_10_binary || encoding == binary) { + if (large) { + return boost::shared_ptr<FieldValue>(new Var32Value(value, 0xa0)); + } else { + return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x90)); + } + } else if (encoding == utf8 && !large) { + return boost::shared_ptr<FieldValue>(new Str16Value(value)); + } else if (encoding == utf16 && !large) { + return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x96)); + } else if (encoding == iso885915 && !large) { + return boost::shared_ptr<FieldValue>(new Var16Value(value, 0x94)); + } else { + //either the string is too large for the encoding in amqp 0-10, or the encoding was not recognised + QPID_LOG(warning, "Could not encode " << value.size() << " byte value as " << encoding << ", encoding as vbin32."); + return boost::shared_ptr<FieldValue>(new Var32Value(value, 0xa0)); + } +} + boost::shared_ptr<FieldValue> toFieldValue(const Variant& in) { boost::shared_ptr<FieldValue> out; @@ -204,8 +229,7 @@ boost::shared_ptr<FieldValue> toFieldValue(const Variant& in) case VAR_INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break; case VAR_FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break; case VAR_DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break; - //TODO: check encoding (and length?) when deciding what AMQP type to treat string as - case VAR_STRING: out = boost::shared_ptr<FieldValue>(new Str16Value(in.asString())); break; + case VAR_STRING: out = convertString(in.asString(), in.getEncoding()); break; case VAR_UUID: out = boost::shared_ptr<FieldValue>(new UuidValue(in.asUuid().data())); break; case VAR_MAP: out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap())); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp index 4242850192..ce4e1ecc2a 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -1,4 +1,4 @@ -/* + /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -20,8 +20,9 @@ */ #include "ConnectionImpl.h" #include "SessionImpl.h" +#include "SimpleUrlParser.h" #include "qpid/messaging/Session.h" -#include "qpid/client/PrivateImplRef.h" +#include "qpid/messaging/PrivateImplRef.h" #include "qpid/framing/Uuid.h" #include "qpid/log/Statement.h" #include <boost/intrusive_ptr.hpp> @@ -33,13 +34,42 @@ namespace amqp0_10 { using qpid::messaging::Variant; using qpid::framing::Uuid; -using namespace qpid::sys; -template <class T> void setIfFound(const Variant::Map& map, const std::string& key, T& value) +void convert(const Variant::List& from, std::vector<std::string>& to) +{ + for (Variant::List::const_iterator i = from.begin(); i != from.end(); ++i) { + to.push_back(i->asString()); + } +} + +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 { + QPID_LOG(debug, "option " << key << " not specified"); + return false; + } +} + +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()) { + if (i->second.getType() == qpid::messaging::VAR_LIST) { + convert(i->second.asList(), value); + } else { + value.push_back(i->second.asString()); + } + return true; + } else { + return false; } } @@ -59,24 +89,47 @@ void convert(const Variant::Map& from, ConnectionSettings& to) setIfFound(from, "max-channels", to.maxChannels); setIfFound(from, "max-frame-size", to.maxFrameSize); setIfFound(from, "bounds", to.bounds); + + setIfFound(from, "protocol", to.protocol); } ConnectionImpl::ConnectionImpl(const Variant::Map& options) : - reconnectionEnabled(true), timeout(-1), - minRetryInterval(1), maxRetryInterval(30) + reconnect(true), timeout(-1), limit(-1), + minReconnectInterval(3), maxReconnectInterval(60), + retries(0) +{ + QPID_LOG(debug, "Created connection with " << options); + setOptions(options); +} + +void ConnectionImpl::setOptions(const Variant::Map& options) { - QPID_LOG(debug, "Opening connection to " << url << " with " << options); convert(options, settings); - setIfFound(options, "reconnection-enabled", reconnectionEnabled); - setIfFound(options, "reconnection-timeout", timeout); - setIfFound(options, "min-retry-interval", minRetryInterval); - setIfFound(options, "max-retry-interval", maxRetryInterval); + 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, "min-reconnect-interval", minReconnectInterval); + setIfFound(options, "max-reconnect-interval", maxReconnectInterval); + } + setIfFound(options, "urls", urls); +} + +void ConnectionImpl::setOption(const std::string& name, const Variant& value) +{ + Variant::Map options; + options[name] = value; + setOptions(options); + QPID_LOG(debug, "Set " << name << " to " << value); } void ConnectionImpl::open(const std::string& u) { - url = u; - connection.open(url, settings); + urls.push_back(u); + connect(); } void ConnectionImpl::close() @@ -97,7 +150,7 @@ void ConnectionImpl::close() boost::intrusive_ptr<SessionImpl> getImplPtr(qpid::messaging::Session& session) { return boost::dynamic_pointer_cast<SessionImpl>( - qpid::client::PrivateImplRef<qpid::messaging::Session>::get(session) + qpid::messaging::PrivateImplRef<qpid::messaging::Session>::get(session) ); } @@ -134,64 +187,65 @@ qpid::messaging::Session ConnectionImpl::newSession(bool transactional, const st try { getImplPtr(impl)->setSession(connection.newSession(name)); } catch (const TransportFailure&) { - reconnect(); + connect(); } return impl; } -void ConnectionImpl::reconnect() +void ConnectionImpl::connect() { - AbsTime start = now(); - ScopedLock<Semaphore> l(semaphore); + qpid::sys::AbsTime start = qpid::sys::now(); + qpid::sys::ScopedLock<qpid::sys::Semaphore> l(semaphore); if (!connection.isOpen()) connect(start); } -bool expired(const AbsTime& start, int timeout) +bool expired(const qpid::sys::AbsTime& start, int64_t timeout) { if (timeout == 0) return true; if (timeout < 0) return false; - Duration used(start, now()); - Duration allowed = timeout * TIME_SEC; - return allowed > used; + qpid::sys::Duration used(start, qpid::sys::now()); + qpid::sys::Duration allowed = timeout * qpid::sys::TIME_SEC; + return allowed < used; } -void ConnectionImpl::connect(const AbsTime& started) +void ConnectionImpl::connect(const qpid::sys::AbsTime& started) { - for (int i = minRetryInterval; !tryConnect(); i = std::min(i * 2, maxRetryInterval)) { - if (expired(started, timeout)) throw TransportFailure(); + for (int64_t i = minReconnectInterval; !tryConnect(); i = std::min(i * 2, maxReconnectInterval)) { + if (!reconnect) throw TransportFailure("Failed to connect (reconnect disabled)"); + if (limit >= 0 && retries++ >= limit) throw TransportFailure("Failed to connect within reconnect limit"); + if (expired(started, timeout)) throw TransportFailure("Failed to connect within reconnect timeout"); else qpid::sys::sleep(i); } + retries = 0; } bool ConnectionImpl::tryConnect() { - if (tryConnect(url) || - (failoverListener.get() && tryConnect(failoverListener->getKnownBrokers()))) - { - return resetSessions(); - } else { - return false; - } + if (tryConnect(urls)) return resetSessions(); + else return false; } -bool ConnectionImpl::tryConnect(const Url& u) +bool ConnectionImpl::tryConnect(const std::vector<std::string>& urls) { - try { - QPID_LOG(info, "Trying to connect to " << url << "..."); - connection.open(u, settings); - failoverListener.reset(new FailoverListener(connection)); - return true; - } catch (const Exception& e) { - //TODO: need to fix timeout on open so that it throws TransportFailure - QPID_LOG(info, "Failed to connect to " << u << ": " << e.what()); - } - return false; -} - -bool ConnectionImpl::tryConnect(const std::vector<Url>& urls) -{ - for (std::vector<Url>::const_iterator i = urls.begin(); i != urls.end(); ++i) { - if (tryConnect(*i)) return true; + for (std::vector<std::string>::const_iterator i = urls.begin(); i != urls.end(); ++i) { + try { + QPID_LOG(info, "Trying to connect to " << *i << "..."); + //TODO: when url support is more complete can avoid this test here + if (i->find("amqp:") == 0) { + Url url(*i); + connection.open(url, settings); + } else { + SimpleUrlParser::parse(*i, settings); + connection.open(settings); + } + QPID_LOG(info, "Connected to " << *i); + return true; + } catch (const Exception& e) { + //TODO: need to fix timeout on + //qpid::client::Connection::open() so that it throws + //TransportFailure rather than a ConnectionException + QPID_LOG(info, "Failed to connect to " << *i << ": " << e.what()); + } } return false; } diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h index d9d0d1e065..37a78b2373 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h @@ -25,7 +25,6 @@ #include "qpid/messaging/Variant.h" #include "qpid/Url.h" #include "qpid/client/Connection.h" -#include "qpid/client/FailoverListener.h" #include "qpid/client/ConnectionSettings.h" #include "qpid/sys/Mutex.h" #include "qpid/sys/Semaphore.h" @@ -46,7 +45,8 @@ class ConnectionImpl : public qpid::messaging::ConnectionImpl qpid::messaging::Session newSession(bool transactional, const std::string& name); qpid::messaging::Session getSession(const std::string& name) const; void closed(SessionImpl&); - void reconnect(); + void connect(); + void setOption(const std::string& name, const qpid::messaging::Variant& value); private: typedef std::map<std::string, qpid::messaging::Session> Sessions; @@ -54,18 +54,19 @@ class ConnectionImpl : public qpid::messaging::ConnectionImpl qpid::sys::Semaphore semaphore;//used to coordinate reconnection Sessions sessions; qpid::client::Connection connection; - std::auto_ptr<FailoverListener> failoverListener; - qpid::Url url; + std::vector<std::string> urls; qpid::client::ConnectionSettings settings; - bool reconnectionEnabled; - int timeout; - int minRetryInterval; - int maxRetryInterval; + bool reconnect; + int64_t timeout; + int32_t limit; + int64_t minReconnectInterval; + int64_t maxReconnectInterval; + int32_t retries; + void setOptions(const qpid::messaging::Variant::Map& options); void connect(const qpid::sys::AbsTime& started); bool tryConnect(); - bool tryConnect(const std::vector<Url>& urls); - bool tryConnect(const Url&); + bool tryConnect(const std::vector<std::string>& urls); bool resetSessions(); }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/FailoverUpdates.cpp b/qpid/cpp/src/qpid/client/amqp0_10/FailoverUpdates.cpp new file mode 100644 index 0000000000..8ef62e4d41 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/FailoverUpdates.cpp @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/FailoverUpdates.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Session.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" +#include "qpid/Url.h" +#include <vector> + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using namespace qpid::messaging; + +struct FailoverUpdatesImpl : qpid::sys::Runnable +{ + Connection connection; + Session session; + Receiver receiver; + qpid::sys::Thread thread; + volatile bool quit; + + FailoverUpdatesImpl(Connection& c) : connection(c), quit(false) + { + session = connection.newSession("failover-updates"); + receiver = session.createReceiver("amq.failover"); + thread = qpid::sys::Thread(*this); + } + + void run() + { + try { + Message message; + while (!quit && receiver.fetch(message)) { + connection.setOption("urls", message.getHeaders()["amq.failover"]); + session.acknowledge(); + } + } catch (const qpid::TransportFailure& e) { + QPID_LOG(warning, "Failover updates stopped on loss of connection. " << e.what()); + } catch (const std::exception& e) { + QPID_LOG(warning, "Failover updates stopped due to exception: " << e.what()); + } + receiver.close(); + session.close(); + } + + void wait() + { + quit = true; + thread.join(); + } +}; + +FailoverUpdates::FailoverUpdates(Connection& connection) : impl(new FailoverUpdatesImpl(connection)) {} +FailoverUpdates::~FailoverUpdates() { if (impl) { impl->wait(); delete impl; } } +FailoverUpdates::FailoverUpdates(const FailoverUpdates&) : impl(0) {} +FailoverUpdates& FailoverUpdates::operator=(const FailoverUpdates&) { return *this; } + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp index e24f2ba5b4..2f52efbceb 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp @@ -57,14 +57,14 @@ qpid::messaging::Message ReceiverImpl::fetch(qpid::messaging::Duration timeout) bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::messaging::Duration timeout) { Get f(*this, message, timeout); - while (!parent.execute(f)) {} + while (!parent->execute(f)) {} return f.result; } bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::messaging::Duration timeout) { Fetch f(*this, message, timeout); - while (!parent.execute(f)) {} + while (!parent->execute(f)) {} return f.result; } @@ -112,7 +112,7 @@ void ReceiverImpl::init(qpid::client::AsyncSession s, AddressResolution& resolve } if (state == CANCELLED) { source->cancel(session, destination); - parent.receiverCancelled(destination); + parent->receiverCancelled(destination); } else { source->subscribe(session, destination); start(); @@ -129,23 +129,23 @@ uint32_t ReceiverImpl::getCapacity() uint32_t ReceiverImpl::available() { - return parent.available(destination); + return parent->available(destination); } uint32_t ReceiverImpl::pendingAck() { - return parent.pendingAck(destination); + return parent->pendingAck(destination); } ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name, const qpid::messaging::Address& a) : - parent(p), destination(name), address(a), byteCredit(0xFFFFFFFF), + parent(&p), destination(name), address(a), byteCredit(0xFFFFFFFF), state(UNRESOLVED), capacity(0), window(0) {} bool ReceiverImpl::getImpl(qpid::messaging::Message& message, qpid::messaging::Duration timeout) { - return parent.get(*this, message, timeout); + return parent->get(*this, message, timeout); } bool ReceiverImpl::fetchImpl(qpid::messaging::Message& message, qpid::messaging::Duration timeout) @@ -172,7 +172,7 @@ void ReceiverImpl::closeImpl() if (state != CANCELLED) { state = CANCELLED; source->cancel(session, destination); - parent.receiverCancelled(destination); + parent->receiverCancelled(destination); } } @@ -188,7 +188,7 @@ void ReceiverImpl::setCapacityImpl(uint32_t c) } qpid::messaging::Session ReceiverImpl::getSession() const { - return qpid::messaging::Session(&parent); + return qpid::messaging::Session(parent.get()); } }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h index 38aa125ec6..689a7f6f25 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h @@ -28,6 +28,7 @@ #include "qpid/client/AsyncSession.h" #include "qpid/client/amqp0_10/SessionImpl.h" #include "qpid/messaging/Duration.h" +#include <boost/intrusive_ptr.hpp> #include <memory> namespace qpid { @@ -65,7 +66,7 @@ class ReceiverImpl : public qpid::messaging::ReceiverImpl void received(qpid::messaging::Message& message); qpid::messaging::Session getSession() const; private: - SessionImpl& parent; + boost::intrusive_ptr<SessionImpl> parent; const std::string destination; const qpid::messaging::Address address; const uint32_t byteCredit; @@ -133,13 +134,13 @@ class ReceiverImpl : public qpid::messaging::ReceiverImpl template <class F> void execute() { F f(*this); - parent.execute(f); + parent->execute(f); } template <class F, class P> void execute1(P p) { F f(*this, p); - parent.execute(f); + parent->execute(f); } }; diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp index 8782e6e813..9bb785e13f 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp @@ -31,17 +31,17 @@ namespace amqp0_10 { SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name, const qpid::messaging::Address& _address) : - parent(_parent), name(_name), address(_address), state(UNRESOLVED), + parent(&_parent), name(_name), address(_address), state(UNRESOLVED), capacity(50), window(0), flushed(false), unreliable(AddressResolution::is_unreliable(address)) {} void SenderImpl::send(const qpid::messaging::Message& message) { if (unreliable) { UnreliableSend f(*this, &message); - parent.execute(f); + parent->execute(f); } else { Send f(*this, &message); - while (f.repeat) parent.execute(f); + while (f.repeat) parent->execute(f); } } @@ -60,7 +60,7 @@ uint32_t SenderImpl::getCapacity() { return capacity; } uint32_t SenderImpl::pending() { CheckPendingSends f(*this, false); - parent.execute(f); + parent->execute(f); return f.pending; } @@ -73,7 +73,7 @@ void SenderImpl::init(qpid::client::AsyncSession s, AddressResolution& resolver) } if (state == CANCELLED) { sink->cancel(session, name); - parent.senderCancelled(name); + parent->senderCancelled(name); } else { sink->declare(session, name); replay(); @@ -140,7 +140,7 @@ void SenderImpl::closeImpl() { state = CANCELLED; sink->cancel(session, name); - parent.senderCancelled(name); + parent->senderCancelled(name); } const std::string& SenderImpl::getName() const @@ -150,7 +150,7 @@ const std::string& SenderImpl::getName() const qpid::messaging::Session SenderImpl::getSession() const { - return qpid::messaging::Session(&parent); + return qpid::messaging::Session(parent.get()); } }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h index b65f8cf8cc..9e4181f42f 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h @@ -28,6 +28,7 @@ #include "qpid/client/AsyncSession.h" #include "qpid/client/amqp0_10/SessionImpl.h" #include <memory> +#include <boost/intrusive_ptr.hpp> #include <boost/ptr_container/ptr_deque.hpp> namespace qpid { @@ -58,7 +59,7 @@ class SenderImpl : public qpid::messaging::SenderImpl qpid::messaging::Session getSession() const; private: - SessionImpl& parent; + boost::intrusive_ptr<SessionImpl> parent; const std::string name; const qpid::messaging::Address address; State state; @@ -143,13 +144,13 @@ class SenderImpl : public qpid::messaging::SenderImpl template <class F> void execute() { F f(*this); - parent.execute(f); + parent->execute(f); } template <class F, class P> bool execute1(P p) { F f(*this, p); - return parent.execute(f); + return parent->execute(f); } }; }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp index 9823dba6e1..65308dd0be 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp @@ -24,7 +24,7 @@ #include "qpid/client/amqp0_10/SenderImpl.h" #include "qpid/client/amqp0_10/MessageSource.h" #include "qpid/client/amqp0_10/MessageSink.h" -#include "qpid/client/PrivateImplRef.h" +#include "qpid/messaging/PrivateImplRef.h" #include "qpid/Exception.h" #include "qpid/log/Statement.h" #include "qpid/messaging/Address.h" @@ -49,7 +49,7 @@ namespace qpid { namespace client { namespace amqp0_10 { -SessionImpl::SessionImpl(ConnectionImpl& c, bool t) : connection(c), transactional(t) {} +SessionImpl::SessionImpl(ConnectionImpl& c, bool t) : connection(&c), transactional(t) {} void SessionImpl::sync() @@ -108,13 +108,13 @@ void SessionImpl::close() for (std::vector<std::string>::const_iterator i = r.begin(); i != r.end(); ++i) getReceiver(*i).close(); - connection.closed(*this); + connection->closed(*this); session.close(); } template <class T, class S> boost::intrusive_ptr<S> getImplPtr(T& t) { - return boost::dynamic_pointer_cast<S>(qpid::client::PrivateImplRef<T>::get(t)); + return boost::dynamic_pointer_cast<S>(qpid::messaging::PrivateImplRef<T>::get(t)); } template <class T> void getFreeKey(std::string& key, T& map) @@ -431,12 +431,12 @@ void SessionImpl::senderCancelled(const std::string& name) void SessionImpl::reconnect() { - connection.reconnect(); + connection->connect(); } qpid::messaging::Connection SessionImpl::getConnection() const { - return qpid::messaging::Connection(&connection); + return qpid::messaging::Connection(connection.get()); } }}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h index 285c8f031b..a7eaae3cdd 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h @@ -29,6 +29,7 @@ #include "qpid/client/amqp0_10/AddressResolution.h" #include "qpid/client/amqp0_10/IncomingMessages.h" #include "qpid/sys/Mutex.h" +#include <boost/intrusive_ptr.hpp> namespace qpid { @@ -106,7 +107,7 @@ class SessionImpl : public qpid::messaging::SessionImpl typedef std::map<std::string, qpid::messaging::Sender> Senders; mutable qpid::sys::Mutex lock; - ConnectionImpl& connection; + boost::intrusive_ptr<ConnectionImpl> connection; qpid::client::Session session; AddressResolution resolver; IncomingMessages incoming; diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp new file mode 100644 index 0000000000..327c2274a6 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SimpleUrlParser.h" +#include "qpid/client/ConnectionSettings.h" +#include "qpid/Exception.h" +#include <boost/lexical_cast.hpp> + +namespace qpid { +namespace client { +namespace amqp0_10 { + +bool split(const std::string& in, char delim, std::pair<std::string, std::string>& result) +{ + std::string::size_type i = in.find(delim); + if (i != std::string::npos) { + result.first = in.substr(0, i); + if (i+1 < in.size()) { + result.second = in.substr(i+1); + } + return true; + } else { + return false; + } +} + +void parseUsernameAndPassword(const std::string& in, qpid::client::ConnectionSettings& result) +{ + std::pair<std::string, std::string> parts; + if (!split(in, '/', parts)) { + result.username = in; + } else { + result.username = parts.first; + result.password = parts.second; + } +} + +void parseHostAndPort(const std::string& in, qpid::client::ConnectionSettings& result) +{ + std::pair<std::string, std::string> parts; + if (!split(in, ':', parts)) { + result.host = in; + } else { + result.host = parts.first; + if (parts.second.size()) { + result.port = boost::lexical_cast<uint16_t>(parts.second); + } + } +} + +void SimpleUrlParser::parse(const std::string& url, qpid::client::ConnectionSettings& result) +{ + std::pair<std::string, std::string> parts; + if (!split(url, '@', parts)) { + parseHostAndPort(url, result); + } else { + parseUsernameAndPassword(parts.first, result); + parseHostAndPort(parts.second, result); + } +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h new file mode 100644 index 0000000000..24f90ca9d6 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h @@ -0,0 +1,42 @@ +#ifndef QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H +#define QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> + +namespace qpid { +namespace client { + +struct ConnectionSettings; + +namespace amqp0_10 { + +/** + * Parses a simple url of the form user/password@hostname:port + */ +struct SimpleUrlParser +{ + static void parse(const std::string& url, qpid::client::ConnectionSettings& result); +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H*/ diff --git a/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp b/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp index 87df187ab2..40c112f534 100644 --- a/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp +++ b/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp @@ -25,6 +25,7 @@ #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/sys/SecurityLayer.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/log/Statement.h" #include "boost/tokenizer.hpp" @@ -33,6 +34,7 @@ namespace qpid { namespace client { using qpid::sys::SecurityLayer; +using qpid::sys::SecuritySettings; using qpid::framing::InternalErrorException; class WindowsSasl : public Sasl @@ -40,7 +42,7 @@ class WindowsSasl : public Sasl public: WindowsSasl(const ConnectionSettings&); ~WindowsSasl(); - std::string start(const std::string& mechanisms, unsigned int ssf); + std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings); std::string step(const std::string& challenge); std::string getMechanism(); std::string getUserId(); @@ -91,7 +93,7 @@ WindowsSasl::~WindowsSasl() } std::string WindowsSasl::start(const std::string& mechanisms, - unsigned int /*ssf*/) + const SecuritySettings* /*externalSettings*/) { QPID_LOG(debug, "WindowsSasl::start(" << mechanisms << ")"); diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp index 460f974b36..f8a875a30c 100644 --- a/qpid/cpp/src/qpid/cluster/Cluster.cpp +++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp @@ -16,7 +16,8 @@ * */ -/** CLUSTER IMPLEMENTATION OVERVIEW +/** + * <h1>CLUSTER IMPLEMENTATION OVERVIEW</h1> * * The cluster works on the principle that if all members of the * cluster receive identical input, they will all produce identical @@ -41,12 +42,15 @@ * * The following are the current areas where broker uses timers or timestamps: * - * - Producer flow control: broker::SemanticState uses connection::getClusterOrderOutput. - * a FrameHandler that sends frames to the client via the cluster. Used by broker::SessionState + * - Producer flow control: broker::SemanticState uses + * connection::getClusterOrderOutput. a FrameHandler that sends + * frames to the client via the cluster. Used by broker::SessionState * - * - QueueCleaner, Message TTL: uses ExpiryPolicy, which is implemented by cluster::ExpiryPolicy. + * - QueueCleaner, Message TTL: uses ExpiryPolicy, which is + * implemented by cluster::ExpiryPolicy. * - * - Connection heartbeat: sends connection controls, not part of session command counting so OK to ignore. + * - Connection heartbeat: sends connection controls, not part of + * session command counting so OK to ignore. * * - LinkRegistry: only cluster elder is ever active for links. * @@ -57,7 +61,10 @@ * * cluster::ExpiryPolicy implements the strategy for message expiry. * - * CLUSTER PROTOCOL OVERVIEW + * ClusterTimer implements periodic timed events in the cluster context. + * Used for periodic management events. + * + * <h1>CLUSTER PROTOCOL OVERVIEW</h1> * * Messages sent to/from CPG are called Events. * @@ -84,12 +91,16 @@ * - Connection control events carrying non-cluster frames: frames sent to the client. * e.g. flow-control frames generated on a timer. * - * CLUSTER INITIALIZATION OVERVIEW + * <h1>CLUSTER INITIALIZATION OVERVIEW</h1> + * + * @see InitialStatusMap * * When a new member joins the CPG group, all members (including the * new one) multicast their "initial status." The new member is in - * INIT mode until it gets a complete set of initial status messages - * from all cluster members. + * PRE_INIT mode until it gets a complete set of initial status + * messages from all cluster members. In a newly-forming cluster is + * then in INIT mode until the configured cluster-size members have + * joined. * * The newcomer uses initial status to determine * - The cluster UUID @@ -97,11 +108,16 @@ * - Do I need to get an update from an existing active member? * - Can I recover from my own store? * - * Initialization happens in the Cluster constructor (plugin - * early-init phase) because it needs to be done before the store - * initializes. In INIT mode sending & receiving from the cluster are - * done single-threaded, bypassing the normal PollableQueues because - * the Poller is not active at this point to service them. + * Pre-initialization happens in the Cluster constructor (plugin + * early-init phase) because it needs to set the recovery flag before + * the store initializes. This phase lasts until inital-status is + * received for all active members. The PollableQueues and Multicaster + * are in "bypass" mode during this phase since the poller has not + * started so there are no threads to serve pollable queues. + * + * The remaining initialization happens in Cluster::initialize() or, + * if cluster-size=N is specified, in the deliver thread when an + * initial-status control is delivered that brings the total to N. */ #include "qpid/Exception.h" #include "qpid/cluster/Cluster.h" @@ -191,16 +207,19 @@ struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler { void initialStatus(uint32_t version, bool active, const Uuid& clusterId, uint8_t storeState, const Uuid& shutdownId, - const framing::SequenceNumber& configSeq, const std::string& firstConfig) { cluster.initialStatus( member, version, active, clusterId, - framing::cluster::StoreState(storeState), shutdownId, configSeq, + framing::cluster::StoreState(storeState), shutdownId, firstConfig, l); } - void ready(const std::string& url) { cluster.ready(member, url, l); } - void configChange(const std::string& current) { cluster.configChange(member, current, l); } + void ready(const std::string& url) { + cluster.ready(member, url, l); + } + void configChange(const std::string& current) { + cluster.configChange(member, current, l); + } void updateOffer(uint64_t updatee) { cluster.updateOffer(member, updatee, l); } @@ -241,7 +260,7 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : quorum(boost::bind(&Cluster::leave, this)), decoder(boost::bind(&Cluster::deliverFrame, this, _1)), discarding(true), - state(INIT), + state(PRE_INIT), initMap(self, settings.size), store(broker.getDataDir().getPath()), elder(false), @@ -271,17 +290,18 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : // without modifying delivery-properties.exchange. broker.getExchanges().registerExchange( boost::shared_ptr<broker::Exchange>(new UpdateExchange(this))); + // Load my store status before we go into initialization if (! broker::NullMessageStore::isNullStore(&broker.getStore())) { store.load(); - if (store.getState() == STORE_STATE_DIRTY_STORE) - broker.setRecovery(false); // Ditch my current store. if (store.getClusterId()) clusterId = store.getClusterId(); // Use stored ID if there is one. QPID_LOG(notice, "Cluster store state: " << store) } - cpg.join(name); + // pump the CPG dispatch manually till we get past PRE_INIT. + while (state == PRE_INIT) + cpg.dispatchOne(); } Cluster::~Cluster() { @@ -298,9 +318,14 @@ void Cluster::initialize() { dispatcher.start(); deliverEventQueue.start(); deliverFrameQueue.start(); + mcast.start(); + + // Run initMapCompleted immediately to process the initial configuration. + assert(state == INIT); + initMapCompleted(*(Mutex::ScopedLock*)0); // Fake lock, single-threaded context. // Add finalizer last for exception safety. - broker.addFinalizer(boost::bind(&Cluster::brokerShutdown, this)); + broker.addFinalizer(boost::bind(&Cluster::brokerShutdown, this)); } // Called in connection thread to insert a client connection. @@ -510,8 +535,13 @@ ConnectionPtr Cluster::getConnection(const EventFrame& e, Lock&) { assert(cp); } else { // New remote connection, create a shadow. - unsigned int ssf = (announce && announce->hasSsf()) ? announce->getSsf() : 0; - cp = new Connection(*this, shadowOut, announce->getManagementId(), id, ssf); + qpid::sys::SecuritySettings secSettings; + if (announce) { + secSettings.ssf = announce->getSsf(); + secSettings.authid = announce->getAuthid(); + secSettings.nodict = announce->getNodict(); + } + cp = new Connection(*this, shadowOut, announce->getManagementId(), id, secSettings); } connections.insert(ConnectionMap::value_type(id, cp)); } @@ -571,9 +601,27 @@ void Cluster::setReady(Lock&) { void Cluster::initMapCompleted(Lock& l) { // Called on completion of the initial status map. QPID_LOG(debug, *this << " initial status map complete. "); - if (state == INIT) { - // We have status for all members so we can make join descisions. + if (state == PRE_INIT) { + // PRE_INIT means we're still in the earlyInitialize phase, in the constructor. + // We decide here whether we want to recover from our store. + // We won't recover if we are joining an active cluster or our store is dirty. + if (store.hasStore() && + (initMap.isActive() || store.getState() == STORE_STATE_DIRTY_STORE)) + broker.setRecovery(false); // Ditch my current store. + state = INIT; + } + else if (state == INIT) { + // INIT means we are past Cluster::initialize(). + + // If we're forming an initial cluster (no active members) + // then we wait to reach the configured cluster-size + if (!initMap.isActive() && initMap.getActualSize() < initMap.getRequiredSize()) { + QPID_LOG(info, *this << initMap.getActualSize() + << " members, waiting for at least " << initMap.getRequiredSize()); + return; + } initMap.checkConsistent(); + elders = initMap.getElders(); QPID_LOG(debug, *this << " elders: " << elders); if (elders.empty()) @@ -626,7 +674,7 @@ void Cluster::configChange(const MemberId&, const std::string& configStr, Lock& mcast.mcastControl( ClusterInitialStatusBody( ProtocolVersion(), CLUSTER_VERSION, state > INIT, clusterId, - store.getState(), store.getShutdownId(), store.getConfigSeq(), + store.getState(), store.getShutdownId(), initMap.getFirstConfigStr() ), self); @@ -668,15 +716,13 @@ struct AppendQueue { // Log a snapshot of broker state, used for debugging inconsistency problems. // May only be called in deliver thread. -void Cluster::debugSnapshot(const char* prefix, Connection* connection) { +std::string Cluster::debugSnapshot() { assertClusterSafe(); std::ostringstream msg; - msg << prefix; - if (connection) msg << " " << connection->getId(); - msg << " snapshot " << map.getFrameSeq() << ":"; + msg << "queue snapshot at " << map.getFrameSeq() << ":"; AppendQueue append(msg); broker.getQueues().eachQueue(append); - QPID_LOG(trace, msg.str()); + return msg.str(); } // Called from Broker::~Broker when broker is shut down. At this @@ -702,7 +748,6 @@ void Cluster::initialStatus(const MemberId& member, uint32_t version, bool activ const framing::Uuid& id, framing::cluster::StoreState store, const framing::Uuid& shutdownId, - const framing::SequenceNumber& configSeq, const std::string& firstConfig, Lock& l) { @@ -715,7 +760,7 @@ void Cluster::initialStatus(const MemberId& member, uint32_t version, bool activ initMap.received( member, ClusterInitialStatusBody(ProtocolVersion(), version, active, id, - store, shutdownId, configSeq, firstConfig) + store, shutdownId, firstConfig) ); if (initMap.transitionToComplete()) initMapCompleted(l); } @@ -762,8 +807,9 @@ void Cluster::updateOffer(const MemberId& updater, uint64_t updateeInt, Lock& l) deliverEventQueue.start(); // Not involved in update. } if (updatee != self && url) { - debugSnapshot("join"); + QPID_LOG(debug, debugSnapshot()); if (mAgent) mAgent->clusterUpdate(); + // Updatee will call clusterUpdate when update completes } } @@ -836,7 +882,7 @@ void Cluster::checkUpdateIn(Lock& l) { if (mAgent) mAgent->suppress(false); // Enable management output. discarding = false; // ok to set, we're stalled for update. QPID_LOG(notice, *this << " update complete, starting catch-up."); - debugSnapshot("initial"); + QPID_LOG(debug, debugSnapshot()); if (mAgent) mAgent->clusterUpdate(); deliverEventQueue.start(); } @@ -963,7 +1009,8 @@ void Cluster::memberUpdate(Lock& l) { std::ostream& operator<<(std::ostream& o, const Cluster& cluster) { static const char* STATE[] = { - "INIT", "JOINER", "UPDATEE", "CATCHUP", "READY", "OFFER", "UPDATER", "LEFT" + "PRE_INIT", "INIT", "JOINER", "UPDATEE", "CATCHUP", + "READY", "OFFER", "UPDATER", "LEFT" }; assert(sizeof(STATE)/sizeof(*STATE) == Cluster::LEFT+1); o << "cluster(" << cluster.self << " " << STATE[cluster.state]; @@ -1003,12 +1050,14 @@ void Cluster::errorCheck(const MemberId& from, uint8_t type, framing::SequenceNu } void Cluster::timerWakeup(const MemberId& , const std::string& name, Lock&) { - timer->deliverWakeup(name); + if (state >= CATCHUP) // Pre catchup our timer isn't set up. + timer->deliverWakeup(name); } void Cluster::timerDrop(const MemberId& , const std::string& name, Lock&) { QPID_LOG(debug, "Cluster timer drop " << map.getFrameSeq() << ": " << name) - timer->deliverDrop(name); + if (state >= CATCHUP) // Pre catchup our timer isn't set up. + timer->deliverDrop(name); } bool Cluster::isElder() const { diff --git a/qpid/cpp/src/qpid/cluster/Cluster.h b/qpid/cpp/src/qpid/cluster/Cluster.h index ffb870606a..4a64ad73d6 100644 --- a/qpid/cpp/src/qpid/cluster/Cluster.h +++ b/qpid/cpp/src/qpid/cluster/Cluster.h @@ -120,8 +120,8 @@ class Cluster : private Cpg::Handler, public management::Manageable { bool isElder() const; - // For debugging only. Can only be called in deliver thread. - void debugSnapshot(const char*, Connection* =0); + // Generates a log message for debugging purposes. + std::string debugSnapshot(); private: typedef sys::Monitor::ScopedLock Lock; @@ -160,7 +160,6 @@ class Cluster : private Cpg::Handler, public management::Manageable { const framing::Uuid& clusterId, framing::cluster::StoreState, const framing::Uuid& shutdownId, - const framing::SequenceNumber& configSeq, const std::string& firstConfig, Lock&); void ready(const MemberId&, const std::string&, Lock&); @@ -181,6 +180,7 @@ class Cluster : private Cpg::Handler, public management::Manageable { void memberUpdate(Lock&); void setClusterId(const framing::Uuid&, Lock&); void erase(const ConnectionId&, Lock&); + void requestUpdate(Lock& ); void initMapCompleted(Lock&); void becomeElder(Lock&); @@ -252,7 +252,8 @@ class Cluster : private Cpg::Handler, public management::Manageable { // Local cluster state, cluster map enum { - INIT, ///< Establishing inital cluster stattus. + PRE_INIT,///< Have not yet received complete initial status map. + INIT, ///< Waiting to reach cluster-size. JOINER, ///< Sent update request, waiting for update offer. UPDATEE, ///< Stalled receive queue at update offer, waiting for update to complete. CATCHUP, ///< Update complete, unstalled but has not yet seen own "ready" event. diff --git a/qpid/cpp/src/qpid/cluster/Connection.cpp b/qpid/cpp/src/qpid/cluster/Connection.cpp index 909ff68d92..1166f685d2 100644 --- a/qpid/cpp/src/qpid/cluster/Connection.cpp +++ b/qpid/cpp/src/qpid/cluster/Connection.cpp @@ -77,9 +77,9 @@ const std::string shadowPrefix("[shadow]"); // Shadow connection Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, const std::string& mgmtId, - const ConnectionId& id, unsigned int ssf) + const ConnectionId& id, const qpid::sys::SecuritySettings& external) : cluster(c), self(id), catchUp(false), output(*this, out), - connectionCtor(&output, cluster.getBroker(), mgmtId, ssf, false, 0, true), + connectionCtor(&output, cluster.getBroker(), mgmtId, external, false, 0, true), expectProtocolHeader(false), mcastFrameHandler(cluster.getMulticast(), self), updateIn(c.getUpdateReceiver()) @@ -88,11 +88,11 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, // Local connection Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, const std::string& mgmtId, MemberId member, - bool isCatchUp, bool isLink, unsigned int ssf + bool isCatchUp, bool isLink, const qpid::sys::SecuritySettings& external ) : cluster(c), self(member, ++idCounter), catchUp(isCatchUp), output(*this, out), connectionCtor(&output, cluster.getBroker(), mgmtId, - ssf, + external, isLink, isCatchUp ? ++catchUpId : 0, isCatchUp), // isCatchUp => shadow @@ -107,7 +107,10 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, QPID_LOG(info, "new client connection " << *this); giveReadCredit(cluster.getSettings().readMax); cluster.getMulticast().mcastControl( - ClusterConnectionAnnounceBody(ProtocolVersion(), mgmtId, getSsf()), getId()); + ClusterConnectionAnnounceBody(ProtocolVersion(), mgmtId, + connectionCtor.external.ssf, + connectionCtor.external.authid, + connectionCtor.external.nodict), getId()); } else { // Catch-up shadow connections initialized using nextShadow id. @@ -122,7 +125,7 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, void Connection::init() { connection = connectionCtor.construct(); QPID_LOG(debug, cluster << " initialized connection: " << *this - << " ssf=" << connection->getSSF()); + << " ssf=" << connection->getExternalSecuritySettings().ssf); if (isLocalClient()) { // Actively send cluster-order frames from local node connection->setClusterOrderOutput(mcastFrameHandler); @@ -142,9 +145,11 @@ void Connection::giveReadCredit(int credit) { output.giveReadCredit(credit); } -void Connection::announce(const std::string& mgmtId, uint32_t ssf) { +void Connection::announce(const std::string& mgmtId, uint32_t ssf, const std::string& authid, bool nodict) { QPID_ASSERT(mgmtId == connectionCtor.mgmtId); - QPID_ASSERT(ssf == connectionCtor.ssf); + QPID_ASSERT(ssf == connectionCtor.external.ssf); + QPID_ASSERT(authid == connectionCtor.external.authid); + QPID_ASSERT(nodict == connectionCtor.external.nodict); init(); } @@ -537,7 +542,7 @@ void Connection::addQueueListener(const std::string& q, uint32_t listener) { void Connection::managementSchema(const std::string& data) { management::ManagementAgent* agent = cluster.getBroker().getManagementAgent(); if (!agent) - throw Exception(QPID_MSG("Management schema update but no management agent.")); + throw Exception(QPID_MSG("Management schema update but management not enabled.")); framing::Buffer buf(const_cast<char*>(data.data()), data.size()); agent->importSchemas(buf); QPID_LOG(debug, cluster << " updated management schemas"); @@ -552,7 +557,7 @@ void Connection::managementSetupState(uint64_t objectNum, uint16_t bootSequence) << objectNum << " seq " << bootSequence); management::ManagementAgent* agent = cluster.getBroker().getManagementAgent(); if (!agent) - throw Exception(QPID_MSG("Management schema update but no management agent.")); + throw Exception(QPID_MSG("Management schema update but management not enabled.")); agent->setNextObjectId(objectNum); agent->setBootSequence(bootSequence); } @@ -560,7 +565,7 @@ void Connection::managementSetupState(uint64_t objectNum, uint16_t bootSequence) void Connection::managementAgents(const std::string& data) { management::ManagementAgent* agent = cluster.getBroker().getManagementAgent(); if (!agent) - throw Exception(QPID_MSG("Management agents update but no management agent.")); + throw Exception(QPID_MSG("Management agent update but management not enabled.")); framing::Buffer buf(const_cast<char*>(data.data()), data.size()); agent->importAgents(buf); QPID_LOG(debug, cluster << " updated management agents"); diff --git a/qpid/cpp/src/qpid/cluster/Connection.h b/qpid/cpp/src/qpid/cluster/Connection.h index 85fad72948..2f72783418 100644 --- a/qpid/cpp/src/qpid/cluster/Connection.h +++ b/qpid/cpp/src/qpid/cluster/Connection.h @@ -34,6 +34,7 @@ #include "qpid/sys/AtomicValue.h" #include "qpid/sys/ConnectionInputHandler.h" #include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/framing/SequenceNumber.h" #include "qpid/framing/FrameDecoder.h" @@ -66,10 +67,10 @@ class Connection : /** Local connection. */ Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, MemberId, bool catchUp, bool isLink, - unsigned int ssf); + const qpid::sys::SecuritySettings& external); /** Shadow connection. */ Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, const ConnectionId& id, - unsigned int ssf); + const qpid::sys::SecuritySettings& external); ~Connection(); ConnectionId getId() const { return self; } @@ -163,7 +164,7 @@ class Connection : void exchange(const std::string& encoded); void giveReadCredit(int credit); - void announce(const std::string& mgmtId, uint32_t ssf); + void announce(const std::string& mgmtId, uint32_t ssf, const std::string& authid, bool nodict); void abort(); void deliverClose(); @@ -174,7 +175,7 @@ class Connection : void managementAgents(const std::string& data); void managementSetupState(uint64_t objectNum, uint16_t bootSequence); - uint32_t getSsf() const { return connectionCtor.ssf; } + //uint32_t getSsf() const { return connectionCtor.external.ssf; } private: struct NullFrameHandler : public framing::FrameHandler { @@ -186,7 +187,7 @@ class Connection : sys::ConnectionOutputHandler* out; broker::Broker& broker; std::string mgmtId; - unsigned int ssf; + qpid::sys::SecuritySettings external; bool isLink; uint64_t objectId; bool shadow; @@ -195,17 +196,17 @@ class Connection : sys::ConnectionOutputHandler* out_, broker::Broker& broker_, const std::string& mgmtId_, - unsigned int ssf_, + const qpid::sys::SecuritySettings& external_, bool isLink_=false, uint64_t objectId_=0, bool shadow_=false - ) : out(out_), broker(broker_), mgmtId(mgmtId_), ssf(ssf_), + ) : out(out_), broker(broker_), mgmtId(mgmtId_), external(external_), isLink(isLink_), objectId(objectId_), shadow(shadow_) {} std::auto_ptr<broker::Connection> construct() { return std::auto_ptr<broker::Connection>( - new broker::Connection(out, broker, mgmtId, ssf, isLink, objectId, shadow)); + new broker::Connection(out, broker, mgmtId, external, isLink, objectId, shadow)); } }; diff --git a/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp b/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp index 8f6f1d9ad5..931cda4893 100644 --- a/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp +++ b/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp @@ -37,26 +37,26 @@ using namespace framing; sys::ConnectionCodec* ConnectionCodec::Factory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, - unsigned int ssf) { + const qpid::sys::SecuritySettings& external) { if (v == ProtocolVersion(0, 10)) - return new ConnectionCodec(v, out, id, cluster, false, false, ssf); + return new ConnectionCodec(v, out, id, cluster, false, false, external); else if (v == ProtocolVersion(0x80 + 0, 0x80 + 10)) // Catch-up connection - return new ConnectionCodec(v, out, id, cluster, true, false, ssf); + return new ConnectionCodec(v, out, id, cluster, true, false, external); return 0; } // Used for outgoing Link connections sys::ConnectionCodec* ConnectionCodec::Factory::create(sys::OutputControl& out, const std::string& logId, - unsigned int ssf) { - return new ConnectionCodec(ProtocolVersion(0,10), out, logId, cluster, false, true, ssf); + const qpid::sys::SecuritySettings& external) { + return new ConnectionCodec(ProtocolVersion(0,10), out, logId, cluster, false, true, external); } ConnectionCodec::ConnectionCodec( const ProtocolVersion& v, sys::OutputControl& out, - const std::string& logId, Cluster& cluster, bool catchUp, bool isLink, unsigned int ssf + const std::string& logId, Cluster& cluster, bool catchUp, bool isLink, const qpid::sys::SecuritySettings& external ) : codec(out, logId, isLink), - interceptor(new Connection(cluster, codec, logId, cluster.getId(), catchUp, isLink, ssf)) + interceptor(new Connection(cluster, codec, logId, cluster.getId(), catchUp, isLink, external)) { std::auto_ptr<sys::ConnectionInputHandler> ih(new ProxyInputHandler(interceptor)); codec.setInputHandler(ih); diff --git a/qpid/cpp/src/qpid/cluster/ConnectionCodec.h b/qpid/cpp/src/qpid/cluster/ConnectionCodec.h index 74cb3c507d..4b919ed351 100644 --- a/qpid/cpp/src/qpid/cluster/ConnectionCodec.h +++ b/qpid/cpp/src/qpid/cluster/ConnectionCodec.h @@ -53,14 +53,14 @@ class ConnectionCodec : public sys::ConnectionCodec { Factory(boost::shared_ptr<sys::ConnectionCodec::Factory> f, Cluster& c) : next(f), cluster(c) {} sys::ConnectionCodec* create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, - unsigned int conn_ssf); + const qpid::sys::SecuritySettings& external); sys::ConnectionCodec* create(sys::OutputControl&, const std::string& id, - unsigned int conn_ssf); + const qpid::sys::SecuritySettings& external); }; ConnectionCodec(const framing::ProtocolVersion&, sys::OutputControl& out, const std::string& logId, Cluster& c, bool catchUp, bool isLink, - unsigned int ssf); + const qpid::sys::SecuritySettings& external); ~ConnectionCodec(); // ConnectionCodec functions. diff --git a/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp b/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp index a1a1456618..c8ecc13f2c 100644 --- a/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp +++ b/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp @@ -86,8 +86,7 @@ bool InitialStatusMap::notInitialized(const Map::value_type& v) { } bool InitialStatusMap::isComplete() const { - return !map.empty() && find_if(map.begin(), map.end(), ¬Initialized) == map.end() - && (map.size() >= size); + return !map.empty() && find_if(map.begin(), map.end(), ¬Initialized) == map.end(); } bool InitialStatusMap::transitionToComplete() { @@ -100,7 +99,7 @@ bool InitialStatusMap::isResendNeeded() { return ret; } -bool InitialStatusMap::isActive(const Map::value_type& v) { +bool InitialStatusMap::isActiveEntry(const Map::value_type& v) { return v.second && v.second->getActive(); } @@ -110,10 +109,15 @@ bool InitialStatusMap::hasStore(const Map::value_type& v) { v.second->getStoreState() == STORE_STATE_DIRTY_STORE); } +bool InitialStatusMap::isActive() { + assert(isComplete()); + return (find_if(map.begin(), map.end(), &isActiveEntry) != map.end()); +} + bool InitialStatusMap::isUpdateNeeded() { assert(isComplete()); // We need an update if there are any active members. - if (find_if(map.begin(), map.end(), &isActive) != map.end()) return true; + if (isActive()) return true; // Otherwise it depends on store status, get my own status: Map::iterator me = map.find(self); @@ -154,7 +158,7 @@ MemberSet InitialStatusMap::getElders() const { Uuid InitialStatusMap::getClusterId() { assert(isComplete()); assert(!map.empty()); - Map::iterator i = find_if(map.begin(), map.end(), &isActive); + Map::iterator i = find_if(map.begin(), map.end(), &isActiveEntry); if (i != map.end()) return i->second->getClusterId(); // An active member else @@ -178,6 +182,7 @@ void InitialStatusMap::checkConsistent() { Uuid clusterId; Uuid shutdownId; + bool initialCluster = !isActive(); for (Map::iterator i = map.begin(); i != map.end(); ++i) { assert(i->second); if (i->second->getActive()) ++active; @@ -193,8 +198,10 @@ void InitialStatusMap::checkConsistent() { ++clean; checkId(clusterId, i->second->getClusterId(), "Cluster-ID mismatch. Stores belong to different clusters."); - checkId(shutdownId, i->second->getShutdownId(), - "Shutdown-ID mismatch. Stores were not shut down together"); + // Only need shutdownId to match if we are in an initially forming cluster. + if (initialCluster) + checkId(shutdownId, i->second->getShutdownId(), + "Shutdown-ID mismatch. Stores were not shut down together"); break; } } @@ -202,10 +209,13 @@ void InitialStatusMap::checkConsistent() { if (none && (clean+dirty+empty)) throw Exception("Mixing transient and persistent brokers in a cluster"); - // If there are no active members and there are dirty stores there - // must be at least one clean store. - if (!active && dirty && !clean) - throw Exception("Cannot recover, no clean store."); + if (map.size() >= size) { + // All initial members are present. If there are no active + // members and there are dirty stores there must be at least + // one clean store. + if (!active && dirty && !clean) + throw Exception("Cannot recover, no clean store."); + } } std::string InitialStatusMap::getFirstConfigStr() const { diff --git a/qpid/cpp/src/qpid/cluster/InitialStatusMap.h b/qpid/cpp/src/qpid/cluster/InitialStatusMap.h index 26a99fa0b0..a5a600365e 100644 --- a/qpid/cpp/src/qpid/cluster/InitialStatusMap.h +++ b/qpid/cpp/src/qpid/cluster/InitialStatusMap.h @@ -31,6 +31,11 @@ namespace cluster { /** * Track status of cluster members during initialization. + * + * When a new member joins the CPG cluster, all members send an initial-status + * control. This map tracks those controls and provides data to make descisions + * about joining the cluster. + * */ class InitialStatusMap { @@ -38,7 +43,7 @@ class InitialStatusMap typedef framing::ClusterInitialStatusBody Status; InitialStatusMap(const MemberId& self, size_t size); - /** Process a config change. @return true if we need to re-send our status */ + /** Process a config change. May make isResendNeeded() true. */ void configChange(const MemberSet& newConfig); /** @return true if we need to re-send status */ bool isResendNeeded(); @@ -46,13 +51,19 @@ class InitialStatusMap /** Process received status */ void received(const MemberId&, const Status& is); - /**@return true if the map is complete. */ + /**@return true if the map has an entry for all current cluster members. */ bool isComplete() const; + + size_t getActualSize() const { return map.size(); } + size_t getRequiredSize() const { return size; } + /**@return true if the map was completed by the last config change or received. */ bool transitionToComplete(); /**@pre isComplete(). @return this node's elders */ MemberSet getElders() const; - /**@pre isComplete(). @return True if we need an update. */ + /**@pre isComplete(). @return True if there are active members of the cluster. */ + bool isActive(); + /**@pre isComplete(). @return True if we need to request an update. */ bool isUpdateNeeded(); /**@pre isComplete(). @return Cluster-wide cluster ID. */ framing::Uuid getClusterId(); @@ -66,8 +77,9 @@ class InitialStatusMap private: typedef std::map<MemberId, boost::optional<Status> > Map; static bool notInitialized(const Map::value_type&); - static bool isActive(const Map::value_type&); + static bool isActiveEntry(const Map::value_type&); static bool hasStore(const Map::value_type&); + Map map; MemberSet firstConfig; MemberId self; diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.cpp b/qpid/cpp/src/qpid/cluster/Multicaster.cpp index 4a8195438f..d57ff76941 100644 --- a/qpid/cpp/src/qpid/cluster/Multicaster.cpp +++ b/qpid/cpp/src/qpid/cluster/Multicaster.cpp @@ -33,10 +33,8 @@ Multicaster::Multicaster(Cpg& cpg_, boost::function<void()> onError_) : onError(onError_), cpg(cpg_), queue(boost::bind(&Multicaster::sendMcast, this, _1), poller), - ready(false) -{ - queue.start(); -} + ready(false), bypass(true) +{} void Multicaster::mcastControl(const framing::AMQBody& body, const ConnectionId& id) { mcast(Event::control(body, id)); @@ -61,10 +59,16 @@ void Multicaster::mcast(const Event& e) { } } QPID_LOG(trace, "MCAST " << e); - queue.push(e); + if (bypass) { // direct, don't queue + iovec iov = e.toIovec(); + // FIXME aconway 2010-03-10: should do limited retry. + while (!cpg.mcast(&iov, 1)) + ; + } + else + queue.push(e); } - Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(const PollableEventQueue::Batch& values) { try { PollableEventQueue::Batch::const_iterator i = values.begin(); @@ -86,6 +90,11 @@ Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(co } } +void Multicaster::start() { + queue.start(); + bypass = false; +} + void Multicaster::setReady() { sys::Mutex::ScopedLock l(lock); ready = true; diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.h b/qpid/cpp/src/qpid/cluster/Multicaster.h index 2db84a9ce0..f70bd5ca31 100644 --- a/qpid/cpp/src/qpid/cluster/Multicaster.h +++ b/qpid/cpp/src/qpid/cluster/Multicaster.h @@ -41,16 +41,18 @@ class Cpg; /** * Multicast to the cluster. Shared, thread safe object. - * - * Runs in two modes; * - * initializing: Hold connection mcast events. Multicast cluster - * events directly in the calling thread. This mode is used before - * joining the cluster where the poller may not yet be active and we - * want to hold any connection traffic till we join. + * holding mode: Hold connection events for later multicast. Cluster + * events are never held. Used during PRE_INIT/INIT state when we + * want to hold any connection traffic till we are read in the + * cluster. + * + * bypass mode: Multicast cluster events directly in the calling + * thread. This mode is used by cluster in PRE_INIT state the poller + * is not yet be active. * - * ready: normal operation. Queues all mcasts on a pollable queue, - * multicasts connection and cluster events. + * Multicaster is created in bypass+holding mode, they are disabled by + * start and setReady respectively. */ class Multicaster { @@ -65,7 +67,9 @@ class Multicaster void mcastBuffer(const char*, size_t, const ConnectionId&); void mcast(const Event& e); - /** Switch to ready mode. */ + /** Start the pollable queue, turn off bypass mode. */ + void start(); + /** Switch to ready mode, release held messages. */ void setReady(); private: @@ -81,6 +85,7 @@ class Multicaster bool ready; PlainEventQueue holdingQueue; std::vector<struct ::iovec> ioVector; + bool bypass; }; }} // namespace qpid::cluster diff --git a/qpid/cpp/src/qpid/cluster/PollableQueue.h b/qpid/cpp/src/qpid/cluster/PollableQueue.h index 2aed6de5b9..59d0bcd36a 100644 --- a/qpid/cpp/src/qpid/cluster/PollableQueue.h +++ b/qpid/cpp/src/qpid/cluster/PollableQueue.h @@ -31,6 +31,13 @@ namespace cluster { /** * More convenient version of PollableQueue that handles iterating * over the batch and error handling. + * + * Constructed in "bypass" mode where items are processed directly + * rather than put on the queue. This is important for the + * PRE_INIT stage when Cluster is pumping CPG dispatch directly + * before the poller has started. + * + * Calling start() starts the pollable queue and disabled bypass mode. */ template <class T> class PollableQueue : public sys::PollableQueue<T> { public: @@ -41,7 +48,7 @@ template <class T> class PollableQueue : public sys::PollableQueue<T> { const boost::shared_ptr<sys::Poller>& poller) : sys::PollableQueue<T>(boost::bind(&PollableQueue<T>::handleBatch, this, _1), poller), - callback(f), error(err), message(msg) + callback(f), error(err), message(msg), bypass(true) {} typename sys::PollableQueue<T>::Batch::const_iterator @@ -62,10 +69,21 @@ template <class T> class PollableQueue : public sys::PollableQueue<T> { } } + void push(const T& t) { + if (bypass) callback(t); + else sys::PollableQueue<T>::push(t); + } + + void start() { + bypass = false; + sys::PollableQueue<T>::start(); + } + private: Callback callback; ErrorCallback error; std::string message; + bool bypass; }; diff --git a/qpid/cpp/src/qpid/cluster/StoreStatus.cpp b/qpid/cpp/src/qpid/cluster/StoreStatus.cpp index 648fcfbbd5..b44c0e1a9a 100644 --- a/qpid/cpp/src/qpid/cluster/StoreStatus.cpp +++ b/qpid/cpp/src/qpid/cluster/StoreStatus.cpp @@ -21,6 +21,7 @@ #include "StoreStatus.h" #include "qpid/Exception.h" #include "qpid/Msg.h" +#include "qpid/log/Statement.h" #include <boost/filesystem/path.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/filesystem/operations.hpp> @@ -54,24 +55,39 @@ Uuid loadUuid(const fs::path& path) { Uuid ret; if (exists(path)) { fs::ifstream i(path); - throw_exceptions(i); - i >> ret; + try { + throw_exceptions(i); + i >> ret; + } catch (const std::exception& e) { + QPID_LOG(error, "Cant load UUID from " << path.string() << ": " << e.what()); + throw; + } } return ret; } void saveUuid(const fs::path& path, const Uuid& uuid) { fs::ofstream o(path); - throw_exceptions(o); - o << uuid; + try { + throw_exceptions(o); + o << uuid; + } catch (const std::exception& e) { + QPID_LOG(error, "Cant save UUID to " << path.string() << ": " << e.what()); + throw; + } } framing::SequenceNumber loadSeqNum(const fs::path& path) { uint32_t n = 0; if (exists(path)) { fs::ifstream i(path); - throw_exceptions(i); - i >> n; + try { + throw_exceptions(i); + i >> n; + } catch (const std::exception& e) { + QPID_LOG(error, "Cant load sequence number from " << path.string() << ": " << e.what()); + throw; + } } return framing::SequenceNumber(n); } @@ -105,9 +121,14 @@ void StoreStatus::save() { create_directory(dir); saveUuid(dir/CLUSTER_ID_FILE, clusterId); saveUuid(dir/SHUTDOWN_ID_FILE, shutdownId); - fs::ofstream o(dir/CONFIG_SEQ_FILE); - throw_exceptions(o); - o << configSeq.getValue(); + try { + fs::ofstream o(dir/CONFIG_SEQ_FILE); + throw_exceptions(o); + o << configSeq.getValue(); + } catch (const std::exception& e) { + QPID_LOG(error, "Cant save sequence number to " << (dir/CONFIG_SEQ_FILE).string() << ": " << e.what()); + throw; + } } catch (const std::exception&e) { throw Exception(QPID_MSG("Cannot save cluster store status: " << e.what())); diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp index 17d856b79c..ab992bf8cf 100644 --- a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp +++ b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp @@ -158,6 +158,12 @@ void UpdateClient::update() { connection.close(); QPID_LOG(debug, updaterId << " update completed to " << updateeId << " at " << updateeUrl << ": " << membership); + // FIXME aconway 2010-03-15: This sleep avoids the race condition + // described in // https://bugzilla.redhat.com/show_bug.cgi?id=568831. + // It allows the connection to fully close before destroying the + // Connection object. Remove when the bug is fixed. + // + sys::usleep(10*1000); // 100ms } namespace { diff --git a/qpid/cpp/src/qpid/framing/FieldValue.cpp b/qpid/cpp/src/qpid/framing/FieldValue.cpp index fd911645f4..ce5a50117c 100644 --- a/qpid/cpp/src/qpid/framing/FieldValue.cpp +++ b/qpid/cpp/src/qpid/framing/FieldValue.cpp @@ -130,6 +130,21 @@ Str16Value::Str16Value(const std::string& v) : reinterpret_cast<const uint8_t*>(v.data()+v.size()))) {} +Var16Value::Var16Value(const std::string& v, uint8_t code) : + FieldValue( + code, + new VariableWidthValue<2>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{} +Var32Value::Var32Value(const std::string& v, uint8_t code) : + FieldValue( + code, + new VariableWidthValue<4>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{} + Struct32Value::Struct32Value(const std::string& v) : FieldValue( 0xAB, diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp index bbb14f98e9..c3cd7e008f 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -96,11 +96,13 @@ ManagementAgent::~ManagementAgent () Mutex::ScopedLock lock (userLock); // Reset the shared pointers to exchanges. If this is not done now, the exchanges - // will stick around until dExchange and mExchange are implicitely destroyed (long + // will stick around until dExchange and mExchange are implicitly destroyed (long // after this destructor completes). Those exchanges hold references to management // objects that will be invalid. dExchange.reset(); mExchange.reset(); + v2Topic.reset(); + v2Direct.reset(); moveNewObjectsLH(); for (ManagementObjectMap::iterator iter = managementObjects.begin (); @@ -183,13 +185,20 @@ void ManagementAgent::writeData () } } -void ManagementAgent::setExchange (qpid::broker::Exchange::shared_ptr _mexchange, - qpid::broker::Exchange::shared_ptr _dexchange) +void ManagementAgent::setExchange(qpid::broker::Exchange::shared_ptr _mexchange, + qpid::broker::Exchange::shared_ptr _dexchange) { mExchange = _mexchange; dExchange = _dexchange; } +void ManagementAgent::setExchangeV2(qpid::broker::Exchange::shared_ptr _texchange, + qpid::broker::Exchange::shared_ptr _dexchange) +{ + v2Topic = _texchange; + v2Direct = _dexchange; +} + void ManagementAgent::registerClass (const string& packageName, const string& className, uint8_t* md5Sum, @@ -210,22 +219,19 @@ void ManagementAgent::registerEvent (const string& packageName, addClassLH(ManagementItem::CLASS_KIND_EVENT, pIter, eventName, md5Sum, schemaCall); } - // Deprecated: -ObjectId ManagementAgent::addObject(ManagementObject* object, uint64_t persistId, bool publishNow) +ObjectId ManagementAgent::addObject(ManagementObject* object, uint64_t persistId) { // always force object to generate key string - return addObject(object, std::string(), persistId != 0, publishNow); + return addObject(object, std::string(), persistId != 0); } ObjectId ManagementAgent::addObject(ManagementObject* object, const std::string& key, - bool persistent, - bool publishNow) + bool persistent) { - Mutex::ScopedLock lock (addLock); uint16_t sequence; sequence = persistent ? 0 : bootSequence; @@ -238,45 +244,21 @@ ObjectId ManagementAgent::addObject(ManagementObject* object, } object->setObjectId(objId); - ManagementObjectMap::iterator destIter = newManagementObjects.find(objId); - if (destIter != newManagementObjects.end()) { - if (destIter->second->isDeleted()) { - newDeletedManagementObjects.push_back(destIter->second); - newManagementObjects.erase(destIter); - } else { - QPID_LOG(error, "ObjectId collision in addObject. class=" << object->getClassName() << - " key=" << objId.getV2Key()); - return objId; - } - } - newManagementObjects[objId] = object; - - if (publishNow) { - ::qpid::messaging::Message m; - ::qpid::messaging::ListContent content(m); - ::qpid::messaging::Variant::List &list_ = content.asList(); - ::qpid::messaging::Variant::Map map_; - ::qpid::messaging::Variant::Map values; - ::qpid::messaging::Variant::Map headers; - - map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(), - object->getClassName(), - "_data", - object->getMd5Sum()); - object->mapEncodeValues(values, true, false); // send props only - map_["_values"] = values; - list_.push_back(map_); - - headers["method"] = "indication"; - headers["qmf.opcode"] = "_data_indication"; - headers["qmf.content"] = "_data"; - headers["qmf.agent"] = std::string(agentName); - content.encode(); - stringstream key; - key << "console.obj.1.0." << object->getPackageName() << "." << object->getClassName(); - sendBuffer(m.getContent(), 0, headers, mExchange, key.str()); - QPID_LOG(trace, "SEND Immediate ContentInd to=" << key.str()); + { + Mutex::ScopedLock lock (addLock); + ManagementObjectMap::iterator destIter = newManagementObjects.find(objId); + if (destIter != newManagementObjects.end()) { + if (destIter->second->isDeleted()) { + newDeletedManagementObjects.push_back(destIter->second); + newManagementObjects.erase(destIter); + } else { + QPID_LOG(error, "ObjectId collision in addObject. class=" << object->getClassName() << + " key=" << objId.getV2Key()); + return objId; + } + } + newManagementObjects[objId] = object; } return objId; @@ -350,11 +332,11 @@ void ManagementAgent::clientAdded (const std::string& routingKey) } void ManagementAgent::clusterUpdate() { - // Called on all cluster memebesr when a new member joins a cluster. + // Called on all cluster memebers when a new member joins a cluster. // Set clientWasAdded so that on the next periodicProcessing we will do // a full update on all cluster members. clientWasAdded = true; - debugSnapshot("update"); + QPID_LOG(debug, "cluster update " << debugSnapshot()); } void ManagementAgent::encodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq) @@ -670,7 +652,7 @@ void ManagementAgent::periodicProcessing (void) sendBuffer (msgBuffer, contentSize, mExchange, routingKey); QPID_LOG(trace, "SEND HeartbeatInd to=" << routingKey); } - debugSnapshot("periodic"); + QPID_LOG(debug, "periodic update " << debugSnapshot()); } void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) @@ -1271,7 +1253,7 @@ void ManagementAgent::handleAttachRequestLH (Buffer& inBuffer, string replyToKey agent->mgmtObject->set_systemId ((const unsigned char*)systemId.data()); agent->mgmtObject->set_brokerBank (brokerBank); agent->mgmtObject->set_agentBank (assignedBank); - addObject (agent->mgmtObject, 0, true); + addObject (agent->mgmtObject, 0); remoteAgents[connectionRef] = agent; QPID_LOG(trace, "Remote Agent registered bank=[" << brokerBank << "." << assignedBank << "] replyTo=" << replyToKey); @@ -1893,13 +1875,13 @@ size_t ManagementAgent::validateEventSchema(Buffer& inBuffer) void ManagementAgent::setAllocator(std::auto_ptr<IdAllocator> a) { - Mutex::ScopedLock lock (addLock); + Mutex::ScopedLock lock (userLock); allocator = a; } uint64_t ManagementAgent::allocateId(Manageable* object) { - Mutex::ScopedLock lock (addLock); + Mutex::ScopedLock lock (userLock); if (allocator.get()) return allocator->getIdFor(object); return 0; } @@ -2031,7 +2013,7 @@ void ManagementAgent::importSchemas(qpid::framing::Buffer& inBuf) { void ManagementAgent::RemoteAgent::mapEncode(qpid::messaging::Variant::Map& map_) const { ::qpid::messaging::VariantMap _objId, _values; - + map_["_brokerBank"] = brokerBank; map_["_agentBank"] = agentBank; map_["_routingKey"] = routingKey; @@ -2068,10 +2050,10 @@ void ManagementAgent::RemoteAgent::mapDecode(const qpid::messaging::Variant::Map mgmtObject->mapDecodeValues(i->second.asMap()); } - agent.addObject(mgmtObject, 0, true); + // TODO aconway 2010-03-04: see comment in encode(), readProperties doesn't set v2key. + mgmtObject->set_connectionRef(connectionRef); } - void ManagementAgent::exportAgents(std::string& out) { ::qpid::messaging::Message m; ::qpid::messaging::ListContent content(m); @@ -2082,15 +2064,12 @@ void ManagementAgent::exportAgents(std::string& out) { i != remoteAgents.end(); ++i) { - ObjectId id = i->first; + // TODO aconway 2010-03-04: see comment in ManagementAgent::RemoteAgent::encode RemoteAgent* agent = i->second; map_.clear(); - omap.clear(); amap.clear(); - id.mapEncode(omap); - map_["_object_id"] = omap; agent->mapEncode(amap); map_["_remote_agent"] = amap; list_.push_back(map_); @@ -2123,16 +2102,16 @@ void ManagementAgent::importAgents(qpid::framing::Buffer& inBuf) { } } -void ManagementAgent::debugSnapshot(const char* type) { +std::string ManagementAgent::debugSnapshot() { std::ostringstream msg; - msg << type << " snapshot, agents:"; + msg << " management snapshot:"; for (RemoteAgentMap::const_iterator i=remoteAgents.begin(); i != remoteAgents.end(); ++i) msg << " " << i->second->routingKey; msg << " packages: " << packages.size(); msg << " objects: " << managementObjects.size(); msg << " new objects: " << newManagementObjects.size(); - QPID_LOG(trace, msg.str()); + return msg.str(); } qpid::messaging::Variant::Map ManagementAgent::toMap(const FieldTable& from) diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.h b/qpid/cpp/src/qpid/management/ManagementAgent.h index 9ec28500c4..675bcb7774 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.h +++ b/qpid/cpp/src/qpid/management/ManagementAgent.h @@ -75,9 +75,12 @@ public: /** Called by cluster to suppress management output during update. */ void suppress(bool s) { suppressed = s; } - void setInterval (uint16_t _interval) { interval = _interval; } - void setExchange (qpid::broker::Exchange::shared_ptr mgmtExchange, - qpid::broker::Exchange::shared_ptr directExchange); + void setInterval(uint16_t _interval) { interval = _interval; } + void setExchange(qpid::broker::Exchange::shared_ptr mgmtExchange, + qpid::broker::Exchange::shared_ptr directExchange); + void setExchangeV2(qpid::broker::Exchange::shared_ptr topicExchange, + qpid::broker::Exchange::shared_ptr directExchange); + int getMaxThreads () { return threadPoolSize; } QPID_BROKER_EXTERN void registerClass (const std::string& packageName, const std::string& className, @@ -88,12 +91,10 @@ public: uint8_t* md5Sum, ManagementObject::writeSchemaCall_t schemaCall); QPID_BROKER_EXTERN ObjectId addObject (ManagementObject* object, - uint64_t persistId = 0, - bool publishNow = false); + uint64_t persistId = 0); QPID_BROKER_EXTERN ObjectId addObject (ManagementObject* object, const std::string& key, - bool persistent = true, - bool publishNow = false); + bool persistent = true); QPID_BROKER_EXTERN void raiseEvent(const ManagementEvent& event, severity_t severity = SEV_DEFAULT); QPID_BROKER_EXTERN void clientAdded (const std::string& routingKey); @@ -238,10 +239,18 @@ private: ManagementObjectVector newDeletedManagementObjects; framing::Uuid uuid; - sys::Mutex addLock; - sys::Mutex userLock; + + // + // Lock hierarchy: If a thread needs to take both addLock and userLock, + // it MUST take userLock first, then addLock. + // + sys::Mutex userLock; + sys::Mutex addLock; + qpid::broker::Exchange::shared_ptr mExchange; qpid::broker::Exchange::shared_ptr dExchange; + qpid::broker::Exchange::shared_ptr v2Topic; + qpid::broker::Exchange::shared_ptr v2Direct; std::string dataDir; uint16_t interval; qpid::broker::Broker* broker; @@ -320,7 +329,7 @@ private: size_t validateSchema(framing::Buffer&, uint8_t kind); size_t validateTableSchema(framing::Buffer&); size_t validateEventSchema(framing::Buffer&); - void debugSnapshot(const char*); + std::string debugSnapshot(); }; }} diff --git a/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp b/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp new file mode 100644 index 0000000000..0813e30891 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/management/ManagementDirectExchange.h" +#include "qpid/log/Statement.h" +#include <assert.h> + +using namespace qpid::management; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +ManagementDirectExchange::ManagementDirectExchange(const string& _name, Manageable* _parent, Broker* b) : + Exchange (_name, _parent, b), DirectExchange(_name, _parent, b) {} +ManagementDirectExchange::ManagementDirectExchange(const std::string& _name, + bool _durable, + const FieldTable& _args, + Manageable* _parent, Broker* b) : + Exchange (_name, _durable, _args, _parent, b), + DirectExchange(_name, _durable, _args, _parent, b) {} + +void ManagementDirectExchange::route(Deliverable& msg, + const string& routingKey, + const FieldTable* args) +{ + bool routeIt = true; + + // TODO: Intercept messages directed to the embedded agent and send them to the management agent. + + if (routeIt) + DirectExchange::route(msg, routingKey, args); +} + +void ManagementDirectExchange::setManagmentAgent(ManagementAgent* agent, int qv) +{ + managementAgent = agent; + qmfVersion = qv; + assert(qmfVersion == 2); // QMFv1 doesn't use a specialized direct exchange +} + + +ManagementDirectExchange::~ManagementDirectExchange() {} + +const std::string ManagementDirectExchange::typeName("management-direct"); + diff --git a/qpid/cpp/src/qpid/management/ManagementDirectExchange.h b/qpid/cpp/src/qpid/management/ManagementDirectExchange.h new file mode 100644 index 0000000000..ab691afa70 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementDirectExchange.h @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ManagementDirectExchange_ +#define _ManagementDirectExchange_ + +#include "qpid/broker/DirectExchange.h" +#include "qpid/management/ManagementAgent.h" + +namespace qpid { +namespace broker { + +class ManagementDirectExchange : public virtual DirectExchange +{ + private: + management::ManagementAgent* managementAgent; + int qmfVersion; + + public: + static const std::string typeName; + + ManagementDirectExchange(const string& name, Manageable* _parent = 0, Broker* broker = 0); + ManagementDirectExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + Manageable* _parent = 0, Broker* broker = 0); + + virtual std::string getType() const { return typeName; } + + virtual void route(Deliverable& msg, + const string& routingKey, + const qpid::framing::FieldTable* args); + + void setManagmentAgent(management::ManagementAgent* agent, int qmfVersion); + + virtual ~ManagementDirectExchange(); +}; + + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/management/ManagementExchange.cpp b/qpid/cpp/src/qpid/management/ManagementExchange.cpp deleted file mode 100644 index b90bcd87d8..0000000000 --- a/qpid/cpp/src/qpid/management/ManagementExchange.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/management/ManagementExchange.h" -#include "qpid/log/Statement.h" - -using namespace qpid::management; -using namespace qpid::broker; -using namespace qpid::framing; -using namespace qpid::sys; - -ManagementExchange::ManagementExchange (const string& _name, Manageable* _parent, Broker* b) : - Exchange (_name, _parent, b), TopicExchange(_name, _parent, b) {} -ManagementExchange::ManagementExchange (const std::string& _name, - bool _durable, - const FieldTable& _args, - Manageable* _parent, Broker* b) : - Exchange (_name, _durable, _args, _parent, b), - TopicExchange(_name, _durable, _args, _parent, b) {} - -void ManagementExchange::route (Deliverable& msg, - const string& routingKey, - const FieldTable* args) -{ - bool routeIt = true; - - // Intercept management agent commands - if ((routingKey.length() > 6 && - routingKey.substr(0, 6).compare("agent.") == 0) || - (routingKey == "broker")) - routeIt = managementAgent->dispatchCommand(msg, routingKey, args); - - if (routeIt) - TopicExchange::route(msg, routingKey, args); -} - -bool ManagementExchange::bind (Queue::shared_ptr queue, - const string& routingKey, - const qpid::framing::FieldTable* args) -{ - managementAgent->clientAdded(routingKey); - return TopicExchange::bind(queue, routingKey, args); -} - -void ManagementExchange::setManagmentAgent (ManagementAgent* agent) -{ - managementAgent = agent; -} - - -ManagementExchange::~ManagementExchange() {} - -const std::string ManagementExchange::typeName("management"); - diff --git a/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp b/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp new file mode 100644 index 0000000000..98650b3adf --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp @@ -0,0 +1,76 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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/management/ManagementTopicExchange.h" +#include "qpid/log/Statement.h" + +using namespace qpid::management; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +ManagementTopicExchange::ManagementTopicExchange(const string& _name, Manageable* _parent, Broker* b) : + Exchange (_name, _parent, b), TopicExchange(_name, _parent, b) {} +ManagementTopicExchange::ManagementTopicExchange(const std::string& _name, + bool _durable, + const FieldTable& _args, + Manageable* _parent, Broker* b) : + Exchange (_name, _durable, _args, _parent, b), + TopicExchange(_name, _durable, _args, _parent, b) {} + +void ManagementTopicExchange::route(Deliverable& msg, + const string& routingKey, + const FieldTable* args) +{ + bool routeIt = true; + + // Intercept management agent commands + if (qmfVersion == 1) { + if ((routingKey.length() > 6 && + routingKey.substr(0, 6).compare("agent.") == 0) || + (routingKey == "broker")) + routeIt = managementAgent->dispatchCommand(msg, routingKey, args); + } + + if (routeIt) + TopicExchange::route(msg, routingKey, args); +} + +bool ManagementTopicExchange::bind(Queue::shared_ptr queue, + const string& routingKey, + const qpid::framing::FieldTable* args) +{ + if (qmfVersion == 1) + managementAgent->clientAdded(routingKey); + return TopicExchange::bind(queue, routingKey, args); +} + +void ManagementTopicExchange::setManagmentAgent(ManagementAgent* agent, int qv) +{ + managementAgent = agent; + qmfVersion = qv; +} + + +ManagementTopicExchange::~ManagementTopicExchange() {} + +const std::string ManagementTopicExchange::typeName("management-topic"); + diff --git a/qpid/cpp/src/qpid/management/ManagementExchange.h b/qpid/cpp/src/qpid/management/ManagementTopicExchange.h index 3fa4039af7..ece1c88ecf 100644 --- a/qpid/cpp/src/qpid/management/ManagementExchange.h +++ b/qpid/cpp/src/qpid/management/ManagementTopicExchange.h @@ -18,8 +18,8 @@ * under the License. * */ -#ifndef _ManagementExchange_ -#define _ManagementExchange_ +#ifndef _ManagementTopicExchange_ +#define _ManagementTopicExchange_ #include "qpid/broker/TopicExchange.h" #include "qpid/management/ManagementAgent.h" @@ -27,32 +27,33 @@ namespace qpid { namespace broker { -class ManagementExchange : public virtual TopicExchange +class ManagementTopicExchange : public virtual TopicExchange { private: management::ManagementAgent* managementAgent; + int qmfVersion; public: static const std::string typeName; - ManagementExchange (const string& name, Manageable* _parent = 0, Broker* broker = 0); - ManagementExchange (const string& _name, bool _durable, - const qpid::framing::FieldTable& _args, - Manageable* _parent = 0, Broker* broker = 0); + ManagementTopicExchange(const string& name, Manageable* _parent = 0, Broker* broker = 0); + ManagementTopicExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + Manageable* _parent = 0, Broker* broker = 0); virtual std::string getType() const { return typeName; } - virtual void route (Deliverable& msg, - const string& routingKey, - const qpid::framing::FieldTable* args); - - virtual bool bind (Queue::shared_ptr queue, + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); - void setManagmentAgent (management::ManagementAgent* agent); + virtual bool bind(Queue::shared_ptr queue, + const string& routingKey, + const qpid::framing::FieldTable* args); + + void setManagmentAgent(management::ManagementAgent* agent, int qmfVersion); - virtual ~ManagementExchange(); + virtual ~ManagementTopicExchange(); }; diff --git a/qpid/cpp/src/qpid/messaging/AddressParser.cpp b/qpid/cpp/src/qpid/messaging/AddressParser.cpp index 265b5fe195..4b29f126f2 100644 --- a/qpid/cpp/src/qpid/messaging/AddressParser.cpp +++ b/qpid/cpp/src/qpid/messaging/AddressParser.cpp @@ -198,6 +198,7 @@ bool AddressParser::readSimpleValue(Variant& value) std::string s; if (readWord(s)) { value = s; + try { value = value.asInt32(); return true; } catch (const InvalidConversion&) {} try { value = value.asInt64(); return true; } catch (const InvalidConversion&) {} try { value = value.asDouble(); return true; } catch (const InvalidConversion&) {} return true; diff --git a/qpid/cpp/src/qpid/messaging/Connection.cpp b/qpid/cpp/src/qpid/messaging/Connection.cpp index 230c9d5dbf..cb06af7693 100644 --- a/qpid/cpp/src/qpid/messaging/Connection.cpp +++ b/qpid/cpp/src/qpid/messaging/Connection.cpp @@ -23,23 +23,17 @@ #include "qpid/messaging/ConnectionImpl.h" #include "qpid/messaging/Session.h" #include "qpid/messaging/SessionImpl.h" -#include "qpid/client/PrivateImplRef.h" +#include "qpid/messaging/PrivateImplRef.h" #include "qpid/client/amqp0_10/ConnectionImpl.h" #include "qpid/log/Statement.h" namespace qpid { -namespace client { - -typedef PrivateImplRef<qpid::messaging::Connection> PI; - -} - namespace messaging { -using qpid::client::PI; +typedef PrivateImplRef<qpid::messaging::Connection> PI; Connection::Connection(ConnectionImpl* impl) { PI::ctor(*this, impl); } -Connection::Connection(const Connection& c) : qpid::client::Handle<ConnectionImpl>() { PI::copy(*this, c); } +Connection::Connection(const Connection& c) : Handle<ConnectionImpl>() { PI::copy(*this, c); } Connection& Connection::operator=(const Connection& c) { return PI::assign(*this, c); } Connection::~Connection() { PI::dtor(*this); } @@ -67,40 +61,11 @@ Session Connection::newSession(bool transactional, const std::string& name) return impl->newSession(transactional, name); } Session Connection::getSession(const std::string& name) const { return impl->getSession(name); } - -InvalidOptionString::InvalidOptionString(const std::string& msg) : Exception(msg) {} - -void parseKeyValuePair(const std::string& in, Variant::Map& out) -{ - std::string::size_type i = in.find('='); - if (i == std::string::npos || i == in.size() || in.find('=', i+1) != std::string::npos) { - throw InvalidOptionString(QPID_MSG("Cannot parse name-value pair from " << in)); - } else { - out[in.substr(0, i)] = in.substr(i+1); - } -} - -void parseOptionString(const std::string& in, Variant::Map& out) -{ - std::string::size_type start = 0; - std::string::size_type i = in.find('&'); - while (i != std::string::npos) { - parseKeyValuePair(in.substr(start, i-start), out); - if (i < in.size()) { - start = i+1; - i = in.find('&', start); - } else { - i = std::string::npos; - } - } - parseKeyValuePair(in.substr(start), out); +void Connection::setOption(const std::string& name, const Variant& value) +{ + impl->setOption(name, value); } -Variant::Map parseOptionString(const std::string& in) -{ - Variant::Map map; - parseOptionString(in, map); - return map; -} +InvalidOptionString::InvalidOptionString(const std::string& msg) : Exception(msg) {} }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/ConnectionImpl.h b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h index 589c9fbe57..2f03c9610b 100644 --- a/qpid/cpp/src/qpid/messaging/ConnectionImpl.h +++ b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h @@ -25,12 +25,10 @@ #include "qpid/RefCounted.h" namespace qpid { -namespace client { -} - namespace messaging { class Session; +class Variant; class ConnectionImpl : public virtual qpid::RefCounted { @@ -40,6 +38,7 @@ class ConnectionImpl : public virtual qpid::RefCounted virtual void close() = 0; virtual Session newSession(bool transactional, const std::string& name) = 0; virtual Session getSession(const std::string& name) const = 0; + virtual void setOption(const std::string& name, const Variant& value) = 0; private: }; }} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/PrivateImplRef.h b/qpid/cpp/src/qpid/messaging/PrivateImplRef.h new file mode 100644 index 0000000000..cc2798c647 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/PrivateImplRef.h @@ -0,0 +1,94 @@ +#ifndef QPID_MESSAGING_PRIVATEIMPL_H +#define QPID_MESSAGING_PRIVATEIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/messaging/ImportExport.h" +#include <boost/intrusive_ptr.hpp> +#include "qpid/RefCounted.h" + +namespace qpid { +namespace messaging { + +// FIXME aconway 2009-04-24: details! +/** @file + * + * Helper class to implement a class with a private, reference counted + * implementation and reference semantics. + * + * Such classes are used in the public API to hide implementation, they + * should. Example of use: + * + * === Foo.h + * + * template <class T> PrivateImplRef; + * class FooImpl; + * + * Foo : public Handle<FooImpl> { + * public: + * Foo(FooImpl* = 0); + * Foo(const Foo&); + * ~Foo(); + * Foo& operator=(const Foo&); + * + * int fooDo(); // and other Foo functions... + * + * private: + * typedef FooImpl Impl; + * Impl* impl; + * friend class PrivateImplRef<Foo>; + * + * === Foo.cpp + * + * typedef PrivateImplRef<Foo> PI; + * Foo::Foo(FooImpl* p) { PI::ctor(*this, p); } + * Foo::Foo(const Foo& c) : Handle<FooImpl>() { PI::copy(*this, c); } + * Foo::~Foo() { PI::dtor(*this); } + * Foo& Foo::operator=(const Foo& c) { return PI::assign(*this, c); } + * + * int foo::fooDo() { return impl->fooDo(); } + * + */ +template <class T> class PrivateImplRef { + public: + typedef typename T::Impl Impl; + typedef boost::intrusive_ptr<Impl> intrusive_ptr; + + static intrusive_ptr get(const T& t) { return intrusive_ptr(t.impl); } + + static void set(T& t, const intrusive_ptr& p) { + if (t.impl == p) return; + if (t.impl) boost::intrusive_ptr_release(t.impl); + t.impl = p.get(); + if (t.impl) boost::intrusive_ptr_add_ref(t.impl); + } + + // Helper functions to implement the ctor, dtor, copy, assign + static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); } + static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); } + static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); } + static T& assign(T& t, const T& x) { set(t, get(x)); return t;} +}; + +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_PRIVATEIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp index 841bee274c..df13052671 100644 --- a/qpid/cpp/src/qpid/messaging/Receiver.cpp +++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp @@ -22,21 +22,15 @@ #include "qpid/messaging/Message.h" #include "qpid/messaging/ReceiverImpl.h" #include "qpid/messaging/Session.h" -#include "qpid/client/PrivateImplRef.h" +#include "qpid/messaging/PrivateImplRef.h" namespace qpid { -namespace client { - -typedef PrivateImplRef<qpid::messaging::Receiver> PI; - -} - namespace messaging { -using qpid::client::PI; +typedef PrivateImplRef<qpid::messaging::Receiver> PI; Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); } -Receiver::Receiver(const Receiver& s) : qpid::client::Handle<ReceiverImpl>() { PI::copy(*this, s); } +Receiver::Receiver(const Receiver& s) : Handle<ReceiverImpl>() { PI::copy(*this, s); } Receiver::~Receiver() { PI::dtor(*this); } Receiver& Receiver::operator=(const Receiver& s) { return PI::assign(*this, s); } bool Receiver::get(Message& message, Duration timeout) { return impl->get(message, timeout); } diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h index a720fe8210..c156265f6c 100644 --- a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h @@ -24,9 +24,6 @@ #include "qpid/RefCounted.h" namespace qpid { -namespace client { -} - namespace messaging { class Message; diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp index 5e18db1d7c..711a857d7a 100644 --- a/qpid/cpp/src/qpid/messaging/Sender.cpp +++ b/qpid/cpp/src/qpid/messaging/Sender.cpp @@ -22,21 +22,14 @@ #include "qpid/messaging/Message.h" #include "qpid/messaging/SenderImpl.h" #include "qpid/messaging/Session.h" -#include "qpid/client/PrivateImplRef.h" +#include "qpid/messaging/PrivateImplRef.h" namespace qpid { -namespace client { - -typedef PrivateImplRef<qpid::messaging::Sender> PI; - -} - namespace messaging { - -using qpid::client::PI; +typedef PrivateImplRef<qpid::messaging::Sender> PI; Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); } -Sender::Sender(const Sender& s) : qpid::client::Handle<SenderImpl>() { PI::copy(*this, s); } +Sender::Sender(const Sender& s) : qpid::messaging::Handle<SenderImpl>() { PI::copy(*this, s); } Sender::~Sender() { PI::dtor(*this); } Sender& Sender::operator=(const Sender& s) { return PI::assign(*this, s); } void Sender::send(const Message& message) { impl->send(message); } diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h index 0294688771..7653049c26 100644 --- a/qpid/cpp/src/qpid/messaging/SenderImpl.h +++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h @@ -24,9 +24,6 @@ #include "qpid/RefCounted.h" namespace qpid { -namespace client { -} - namespace messaging { class Message; diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp index 5d1a6fb815..2ac19727e3 100644 --- a/qpid/cpp/src/qpid/messaging/Session.cpp +++ b/qpid/cpp/src/qpid/messaging/Session.cpp @@ -25,21 +25,15 @@ #include "qpid/messaging/Sender.h" #include "qpid/messaging/Receiver.h" #include "qpid/messaging/SessionImpl.h" -#include "qpid/client/PrivateImplRef.h" +#include "qpid/messaging/PrivateImplRef.h" namespace qpid { -namespace client { - -typedef PrivateImplRef<qpid::messaging::Session> PI; - -} - namespace messaging { -using qpid::client::PI; +typedef PrivateImplRef<qpid::messaging::Session> PI; Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); } -Session::Session(const Session& s) : qpid::client::Handle<SessionImpl>() { PI::copy(*this, s); } +Session::Session(const Session& s) : Handle<SessionImpl>() { PI::copy(*this, s); } Session::~Session() { PI::dtor(*this); } Session& Session::operator=(const Session& s) { return PI::assign(*this, s); } void Session::commit() { impl->commit(); } diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h index 653df8fdda..79f0d007b5 100644 --- a/qpid/cpp/src/qpid/messaging/SessionImpl.h +++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h @@ -26,9 +26,6 @@ #include "qpid/messaging/Duration.h" namespace qpid { -namespace client { -} - namespace messaging { class Address; diff --git a/qpid/cpp/src/qpid/messaging/Variant.cpp b/qpid/cpp/src/qpid/messaging/Variant.cpp index ba93f160ec..2567b7508b 100644 --- a/qpid/cpp/src/qpid/messaging/Variant.cpp +++ b/qpid/cpp/src/qpid/messaging/Variant.cpp @@ -20,6 +20,7 @@ */ #include "qpid/messaging/Variant.h" #include "qpid/Msg.h" +#include "qpid/log/Statement.h" #include <boost/format.hpp> #include <boost/lexical_cast.hpp> #include <algorithm> @@ -50,7 +51,7 @@ class VariantImpl VariantImpl(int64_t); VariantImpl(float); VariantImpl(double); - VariantImpl(const std::string&); + VariantImpl(const std::string&, const std::string& encoding=std::string()); VariantImpl(const Variant::Map&); VariantImpl(const Variant::List&); VariantImpl(const Uuid&); @@ -130,7 +131,8 @@ VariantImpl::VariantImpl(int32_t i) : type(VAR_INT32) { value.i32 = i; } VariantImpl::VariantImpl(int64_t i) : type(VAR_INT64) { value.i64 = i; } VariantImpl::VariantImpl(float f) : type(VAR_FLOAT) { value.f = f; } VariantImpl::VariantImpl(double d) : type(VAR_DOUBLE) { value.d = d; } -VariantImpl::VariantImpl(const std::string& s) : type(VAR_STRING) { value.v = new std::string(s); } +VariantImpl::VariantImpl(const std::string& s, const std::string& e) + : type(VAR_STRING), encoding(e) { value.v = new std::string(s); } VariantImpl::VariantImpl(const Variant::Map& m) : type(VAR_MAP) { value.v = new Variant::Map(m); } VariantImpl::VariantImpl(const Variant::List& l) : type(VAR_LIST) { value.v = new Variant::List(l); } VariantImpl::VariantImpl(const Uuid& u) : type(VAR_UUID) { value.v = new Uuid(u); } @@ -448,7 +450,7 @@ VariantImpl* VariantImpl::create(const Variant& v) case VAR_INT64: return new VariantImpl(v.asInt64()); case VAR_FLOAT: return new VariantImpl(v.asFloat()); case VAR_DOUBLE: return new VariantImpl(v.asDouble()); - case VAR_STRING: return new VariantImpl(v.asString()); + case VAR_STRING: return new VariantImpl(v.asString(), v.getEncoding()); case VAR_MAP: return new VariantImpl(v.asMap()); case VAR_LIST: return new VariantImpl(v.asList()); case VAR_UUID: return new VariantImpl(v.asUuid()); diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp index f658b7d50f..5771141d08 100644 --- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp +++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp @@ -22,6 +22,7 @@ #include "qpid/sys/AsynchIOHandler.h" #include "qpid/sys/AsynchIO.h" #include "qpid/sys/Socket.h" +#include "qpid/sys/SecuritySettings.h" #include "qpid/framing/AMQP_HighestVersion.h" #include "qpid/framing/ProtocolInitiation.h" #include "qpid/log/Statement.h" @@ -144,7 +145,7 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { decoded = in.getPosition(); QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); try { - codec = factory->create(protocolInit.getVersion(), *this, identifier, 0); + codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings()); if (!codec) { //TODO: may still want to revise this... //send valid version header & close connection. @@ -200,7 +201,7 @@ void AsynchIOHandler::nobuffs(AsynchIO&) { void AsynchIOHandler::idle(AsynchIO&){ if (isClient && codec == 0) { - codec = factory->create(*this, identifier, 0); + codec = factory->create(*this, identifier, SecuritySettings()); write(framing::ProtocolInitiation(codec->getVersion())); return; } diff --git a/qpid/cpp/src/qpid/sys/ConnectionCodec.h b/qpid/cpp/src/qpid/sys/ConnectionCodec.h index 7231b1daa6..c2890f06dc 100644 --- a/qpid/cpp/src/qpid/sys/ConnectionCodec.h +++ b/qpid/cpp/src/qpid/sys/ConnectionCodec.h @@ -30,6 +30,7 @@ namespace sys { class InputHandlerFactory; class OutputControl; +struct SecuritySettings; /** * Interface of coder/decoder for a connection of a specific protocol @@ -49,27 +50,15 @@ class ConnectionCodec : public Codec { struct Factory { virtual ~Factory() {} - /** Security Strength Factor - indicates the level of security provided - * by the underlying transport. If zero, the transport provides no - * security (e.g. TCP). If non-zero, the transport provides some level - * of security (e.g. SSL). The values for SSF can be interpreted as: - * - * 0 = No protection. - * 1 = Integrity checking only. - * >1 = Supports authentication, integrity and confidentiality. - * The number represents the encryption key length. - */ - /** Return 0 if version unknown */ virtual ConnectionCodec* create( framing::ProtocolVersion, OutputControl&, const std::string& id, - unsigned int conn_ssf + const SecuritySettings& ) = 0; /** Return "preferred" codec for outbound connections. */ virtual ConnectionCodec* create( - OutputControl&, const std::string& id, - unsigned int conn_ssf + OutputControl&, const std::string& id, const SecuritySettings& ) = 0; }; }; diff --git a/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp index b325931793..5a5c10401c 100644 --- a/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp +++ b/qpid/cpp/src/qpid/sys/RdmaIOPlugin.cpp @@ -27,6 +27,7 @@ #include "qpid/log/Statement.h" #include "qpid/sys/rdma/RdmaIO.h" #include "qpid/sys/OutputControl.h" +#include "qpid/sys/SecuritySettings.h" #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> @@ -139,7 +140,7 @@ void RdmaIOHandler::initProtocolOut() { // but we must be able to send assert( codec == 0 ); assert( aio->writable() && aio->bufferAvailable() ); - codec = factory->create(*this, identifier, 0); + codec = factory->create(*this, identifier, SecuritySettings()); write(framing::ProtocolInitiation(codec->getVersion())); } @@ -186,7 +187,7 @@ void RdmaIOHandler::initProtocolIn(Rdma::Buffer* buff) { decoded = in.getPosition(); QPID_LOG(debug, "Rdma: RECV [" << identifier << "] INIT(" << protocolInit << ")"); - codec = factory->create(protocolInit.getVersion(), *this, identifier, 0); + codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings()); // If we failed to create the codec then we don't understand the offered protocol version if (!codec) { diff --git a/qpid/cpp/src/qpid/sys/SecuritySettings.h b/qpid/cpp/src/qpid/sys/SecuritySettings.h new file mode 100644 index 0000000000..bfcd08fd0f --- /dev/null +++ b/qpid/cpp/src/qpid/sys/SecuritySettings.h @@ -0,0 +1,58 @@ +#ifndef QPID_SYS_SECURITYSETTINGS_H +#define QPID_SYS_SECURITYSETTINGS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace sys { + +/** + * Conveys security information from a given transport to the upper + * layers. + */ +struct SecuritySettings +{ + /** + * Security Strength Factor (SSF). Possible values are: + * + * @li 0 No security + * @li 1 Integrity checking only + * @li >1 Integrity and confidentiality with the number + * giving the encryption key length. + */ + unsigned int ssf; + /** + * An authorisation id + */ + std::string authid; + + /** + * Disables SASL mechanisms that are vulnerable to passive + * dictionary-based password attacks + */ + bool nodict; + + SecuritySettings() : ssf(0), nodict(false) {} +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_SECURITYSETTINGS_H*/ diff --git a/qpid/cpp/src/qpid/sys/SslPlugin.cpp b/qpid/cpp/src/qpid/sys/SslPlugin.cpp index c143f1f1d0..297787f497 100644 --- a/qpid/cpp/src/qpid/sys/SslPlugin.cpp +++ b/qpid/cpp/src/qpid/sys/SslPlugin.cpp @@ -41,14 +41,18 @@ struct SslServerOptions : ssl::SslOptions { uint16_t port; bool clientAuth; + bool nodict; SslServerOptions() : port(5671), - clientAuth(false) + clientAuth(false), + nodict(false) { addOptions() ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections") ("ssl-require-client-authentication", optValue(clientAuth), - "Forces clients to authenticate in order to establish an SSL connection"); + "Forces clients to authenticate in order to establish an SSL connection") + ("ssl-sasl-no-dict", optValue(nodict), + "Disables SASL mechanisms that are vulnerable to passive dictionary-based password attacks"); } }; @@ -57,6 +61,7 @@ class SslProtocolFactory : public ProtocolFactory { qpid::sys::ssl::SslSocket listener; const uint16_t listeningPort; std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor; + bool nodict; public: SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay); @@ -97,7 +102,8 @@ static struct SslPlugin : public Plugin { const broker::Broker::Options& opts = broker->getOptions(); ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options, - opts.connectionBacklog, opts.tcpNoDelay)); + opts.connectionBacklog, + opts.tcpNoDelay)); QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort()); broker->registerProtocolFactory("ssl", protocol); } catch (const std::exception& e) { @@ -109,12 +115,13 @@ static struct SslPlugin : public Plugin { } sslPlugin; SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, int backlog, bool nodelay) : - tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)) + tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)), + nodict(options.nodict) {} void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys::ssl::SslSocket& s, ConnectionCodec::Factory* f, bool isClient) { - qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getPeerAddress(), f); + qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getPeerAddress(), f, nodict); if (tcpNoDelay) { s.setTcpNoDelay(tcpNoDelay); diff --git a/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp b/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp index 3469f88c0f..5516d72065 100644 --- a/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp +++ b/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp @@ -42,13 +42,14 @@ struct Buff : public SslIO::BufferBase { { delete [] bytes;} }; -SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f) : +SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f, bool _nodict) : identifier(id), aio(0), factory(f), codec(0), readError(false), - isClient(false) + isClient(false), + nodict(_nodict) {} SslHandler::~SslHandler() { @@ -111,7 +112,7 @@ void SslHandler::readbuff(SslIO& , SslIO::BufferBase* buff) { decoded = in.getPosition(); QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); try { - codec = factory->create(protocolInit.getVersion(), *this, identifier, aio->getKeyLen()); + codec = factory->create(protocolInit.getVersion(), *this, identifier, getSecuritySettings(aio)); if (!codec) { //TODO: may still want to revise this... //send valid version header & close connection. @@ -166,7 +167,7 @@ void SslHandler::nobuffs(SslIO&) { void SslHandler::idle(SslIO&){ if (isClient && codec == 0) { - codec = factory->create(*this, identifier, aio->getKeyLen()); + codec = factory->create(*this, identifier, getSecuritySettings(aio)); write(framing::ProtocolInitiation(codec->getVersion())); return; } @@ -183,5 +184,12 @@ void SslHandler::idle(SslIO&){ aio->queueWriteClose(); } +SecuritySettings SslHandler::getSecuritySettings(SslIO* aio) +{ + SecuritySettings settings = aio->getSecuritySettings(); + settings.nodict = nodict; + return settings; +} + }}} // namespace qpid::sys::ssl diff --git a/qpid/cpp/src/qpid/sys/ssl/SslHandler.h b/qpid/cpp/src/qpid/sys/ssl/SslHandler.h index 8f6b8e732a..a340109966 100644 --- a/qpid/cpp/src/qpid/sys/ssl/SslHandler.h +++ b/qpid/cpp/src/qpid/sys/ssl/SslHandler.h @@ -45,11 +45,13 @@ class SslHandler : public OutputControl { ConnectionCodec* codec; bool readError; bool isClient; + bool nodict; void write(const framing::ProtocolInitiation&); + qpid::sys::SecuritySettings getSecuritySettings(SslIO* aio); public: - SslHandler(std::string id, ConnectionCodec::Factory* f); + SslHandler(std::string id, ConnectionCodec::Factory* f, bool nodict); ~SslHandler(); void init(SslIO* a, int numBuffs); diff --git a/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp b/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp index c149d6ea74..a57123c182 100644 --- a/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp +++ b/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp @@ -436,4 +436,9 @@ void SslIO::close(DispatchHandle& h) { } } -int SslIO::getKeyLen() {return socket.getKeyLen();} +SecuritySettings SslIO::getSecuritySettings() { + SecuritySettings settings; + settings.ssf = socket.getKeyLen(); + settings.authid = socket.getClientAuthId(); + return settings; +} diff --git a/qpid/cpp/src/qpid/sys/ssl/SslIo.h b/qpid/cpp/src/qpid/sys/ssl/SslIo.h index 3162abac40..53ac69d8d6 100644 --- a/qpid/cpp/src/qpid/sys/ssl/SslIo.h +++ b/qpid/cpp/src/qpid/sys/ssl/SslIo.h @@ -22,6 +22,7 @@ */ #include "qpid/sys/DispatchHandle.h" +#include "qpid/sys/SecuritySettings.h" #include <boost/function.hpp> #include <deque> @@ -156,7 +157,7 @@ public: bool writeQueueEmpty() { return writeQueue.empty(); } BufferBase* getQueuedBuffer(); - int getKeyLen(); + qpid::sys::SecuritySettings getSecuritySettings(); private: ~SslIO(); diff --git a/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp b/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp index aa8cf127d7..22b0909ad4 100644 --- a/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp +++ b/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp @@ -102,6 +102,34 @@ std::string getService(int fd, bool local) return servName; } +const std::string DOMAIN_SEPARATOR("@"); +const std::string DC_SEPARATOR("."); +const std::string DC("DC"); +const std::string DN_DELIMS(" ,="); + +std::string getDomainFromSubject(std::string subject) +{ + std::string::size_type last = subject.find_first_not_of(DN_DELIMS, 0); + std::string::size_type i = subject.find_first_of(DN_DELIMS, last); + + std::string domain; + bool nextTokenIsDC = false; + while (std::string::npos != i || std::string::npos != last) + { + std::string token = subject.substr(last, i - last); + if (nextTokenIsDC) { + if (domain.size()) domain += DC_SEPARATOR; + domain += token; + nextTokenIsDC = false; + } else if (token == DC) { + nextTokenIsDC = true; + } + last = subject.find_first_not_of(DN_DELIMS, i); + i = subject.find_first_of(DN_DELIMS, last); + } + return domain; +} + } SslSocket::SslSocket() : IOHandle(new IOHandlePrivate()), socket(0), prototype(0) @@ -294,4 +322,25 @@ int SslSocket::getKeyLen() const return 0; } +std::string SslSocket::getClientAuthId() const +{ + std::string authId; + CERTCertificate* cert = SSL_PeerCertificate(socket); + if (cert) { + authId = CERT_GetCommonName(&(cert->subject)); + /* + * The NSS function CERT_GetDomainComponentName only returns + * the last component of the domain name, so we have to parse + * the subject manually to extract the full domain. + */ + std::string domain = getDomainFromSubject(cert->subjectName); + if (!domain.empty()) { + authId += DOMAIN_SEPARATOR; + authId += domain; + } + CERT_DestroyCertificate(cert); + } + return authId; +} + }}} // namespace qpid::sys::ssl diff --git a/qpid/cpp/src/qpid/sys/ssl/SslSocket.h b/qpid/cpp/src/qpid/sys/ssl/SslSocket.h index f1f05e7a98..e2443e31c8 100644 --- a/qpid/cpp/src/qpid/sys/ssl/SslSocket.h +++ b/qpid/cpp/src/qpid/sys/ssl/SslSocket.h @@ -101,6 +101,7 @@ public: int getError() const; int getKeyLen() const; + std::string getClientAuthId() const; private: mutable std::string connectname; diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/qpid/cpp/src/qpid/xml/XmlExchange.cpp index 6771767969..fbf7566a18 100644 --- a/qpid/cpp/src/qpid/xml/XmlExchange.cpp +++ b/qpid/cpp/src/qpid/xml/XmlExchange.cpp @@ -34,6 +34,10 @@ #include <xercesc/framework/MemBufInputSource.hpp> +#ifdef XQ_EFFECTIVE_BOOLEAN_VALUE_HPP +#include <xqilla/ast/XQEffectiveBooleanValue.hpp> +#endif + #include <xqilla/ast/XQGlobalVariable.hpp> #include <xqilla/context/ItemFactory.hpp> @@ -51,7 +55,7 @@ namespace qpid { namespace broker { - XmlExchange::XmlExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b) +XmlExchange::XmlExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); @@ -180,7 +184,13 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F } Result result = query->execute(context.get()); +#ifdef XQ_EFFECTIVE_BOOLEAN_VALUE_HPP + Item::Ptr first_ = result->next(context.get()); + Item::Ptr second_ = result->next(context.get()); + return XQEffectiveBooleanValue::get(first_, second_, context.get(), 0); +#else return result->getEffectiveBooleanValue(context.get(), 0); +#endif } catch (XQException& e) { QPID_LOG(warning, "Could not parse XML content (or message headers):" << msgContent); diff --git a/qpid/cpp/src/tests/ClusterFixture.cpp b/qpid/cpp/src/tests/ClusterFixture.cpp index fd90ed170e..b7e8e88abf 100644 --- a/qpid/cpp/src/tests/ClusterFixture.cpp +++ b/qpid/cpp/src/tests/ClusterFixture.cpp @@ -130,11 +130,11 @@ void ClusterFixture::kill(size_t n, int sig) { forkedBrokers[n]->kill(sig); } -/** Kill a broker and suppressing errors from closing connection c. */ +/** Kill a broker and suppress errors from closing connection c. */ void ClusterFixture::killWithSilencer(size_t n, client::Connection& c, int sig) { ScopedSuppressLogging sl; - kill(n,sig); try { c.close(); } catch(...) {} + kill(n,sig); } /** diff --git a/qpid/cpp/src/tests/InitialStatusMap.cpp b/qpid/cpp/src/tests/InitialStatusMap.cpp index dc86c41103..ecbe2d4161 100644 --- a/qpid/cpp/src/tests/InitialStatusMap.cpp +++ b/qpid/cpp/src/tests/InitialStatusMap.cpp @@ -37,19 +37,19 @@ QPID_AUTO_TEST_SUITE(InitialStatusMapTestSuite) typedef InitialStatusMap::Status Status; Status activeStatus(const Uuid& id=Uuid(), const MemberSet& ms=MemberSet()) { - return Status(ProtocolVersion(), 0, true, id, STORE_STATE_NO_STORE, Uuid(), 0, + return Status(ProtocolVersion(), 0, true, id, STORE_STATE_NO_STORE, Uuid(), encodeMemberSet(ms)); } Status newcomerStatus(const Uuid& id=Uuid(), const MemberSet& ms=MemberSet()) { - return Status(ProtocolVersion(), 0, false, id, STORE_STATE_NO_STORE, Uuid(), 0, + return Status(ProtocolVersion(), 0, false, id, STORE_STATE_NO_STORE, Uuid(), encodeMemberSet(ms)); } Status storeStatus(bool active, StoreState state, Uuid start=Uuid(), Uuid stop=Uuid(), const MemberSet& ms=MemberSet()) { - return Status(ProtocolVersion(), 0, active, start, state, stop, 0, + return Status(ProtocolVersion(), 0, active, start, state, stop, encodeMemberSet(ms)); } @@ -173,20 +173,6 @@ QPID_AUTO_TEST_CASE(testInteveningConfig) { BOOST_CHECK_EQUAL(map.getClusterId(), id); } -QPID_AUTO_TEST_CASE(testInitialSize) { - InitialStatusMap map(MemberId(0), 3); - map.configChange(list_of<MemberId>(0)(1)); - map.received(MemberId(0), newcomerStatus()); - map.received(MemberId(1), newcomerStatus()); - BOOST_CHECK(!map.isComplete()); - - map.configChange(list_of<MemberId>(0)(1)(2)); - map.received(MemberId(0), newcomerStatus()); - map.received(MemberId(1), newcomerStatus()); - map.received(MemberId(2), newcomerStatus()); - BOOST_CHECK(map.isComplete()); -} - QPID_AUTO_TEST_CASE(testAllCleanNoUpdate) { InitialStatusMap map(MemberId(0), 3); map.configChange(list_of<MemberId>(0)(1)(2)); @@ -244,8 +230,6 @@ QPID_AUTO_TEST_CASE(testEmptyAlone) { BOOST_CHECK(!map.isUpdateNeeded()); } -// FIXME aconway 2009-11-20: consistency tests for mixed stores, - QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index 1dc6c9a0e2..9c1a761062 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -17,8 +17,6 @@ # under the License. # -SUBDIRS = . testagent - AM_CXXFLAGS = $(WARNING_CFLAGS) -DBOOST_TEST_DYN_LINK INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src PUBLIC_INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include # Use public API only @@ -381,3 +379,5 @@ python_prep: --prefix=$(PYTHON_BLD_DIR) --install-lib=$(PYTHON_BLD_DIR) \ --install-scripts=$(PYTHON_BLD_DIR)/commands; \ else echo "WARNING: python client not built, missing $(PYTHON_SRC_DIR)"; fi + +include testagent.mk diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp index dea98216b4..a1e90f83f3 100644 --- a/qpid/cpp/src/tests/MessagingSessionTests.cpp +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -336,6 +336,12 @@ QPID_AUTO_TEST_CASE(testMapMessage) MapContent content(out); content["abc"] = "def"; content["pi"] = 3.14f; + Variant utf8("A utf 8 string"); + utf8.setEncoding("utf8"); + content["utf8"] = utf8; + Variant utf16("\x00\x61\x00\x62\x00\x63"); + utf16.setEncoding("utf16"); + content["utf16"] = utf16; content.encode(); sender.send(out); Receiver receiver = fix.session.createReceiver(fix.queue); @@ -343,6 +349,10 @@ QPID_AUTO_TEST_CASE(testMapMessage) MapView view(in); BOOST_CHECK_EQUAL(view["abc"].asString(), "def"); BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f); + BOOST_CHECK_EQUAL(view["utf8"].asString(), utf8.asString()); + BOOST_CHECK_EQUAL(view["utf8"].getEncoding(), utf8.getEncoding()); + BOOST_CHECK_EQUAL(view["utf16"].asString(), utf16.asString()); + BOOST_CHECK_EQUAL(view["utf16"].getEncoding(), utf16.getEncoding()); fix.session.acknowledge(); } diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp index c0bb9772c8..db9e419eab 100644 --- a/qpid/cpp/src/tests/Variant.cpp +++ b/qpid/cpp/src/tests/Variant.cpp @@ -178,6 +178,21 @@ QPID_AUTO_TEST_CASE(testIsEqualTo) BOOST_CHECK_EQUAL(a, b); } +QPID_AUTO_TEST_CASE(testEncoding) +{ + Variant a("abc"); + a.setEncoding("utf8"); + Variant b = a; + Variant map = Variant::Map(); + map.asMap()["a"] = a; + map.asMap()["b"] = b; + BOOST_CHECK_EQUAL(a.getEncoding(), std::string("utf8")); + BOOST_CHECK_EQUAL(a.getEncoding(), b.getEncoding()); + BOOST_CHECK_EQUAL(a.getEncoding(), map.asMap()["a"].getEncoding()); + BOOST_CHECK_EQUAL(b.getEncoding(), map.asMap()["b"].getEncoding()); + BOOST_CHECK_EQUAL(map.asMap()["a"].getEncoding(), map.asMap()["b"].getEncoding()); +} + QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/cluster_tests.fail b/qpid/cpp/src/tests/cluster_tests.fail index 268795642d..b28b04f643 100644 --- a/qpid/cpp/src/tests/cluster_tests.fail +++ b/qpid/cpp/src/tests/cluster_tests.fail @@ -1,3 +1,3 @@ -cluster_tests.LongTests.test_management + diff --git a/qpid/cpp/src/tests/cluster_tests.py b/qpid/cpp/src/tests/cluster_tests.py index 276ad1af2d..4fefe26db3 100755 --- a/qpid/cpp/src/tests/cluster_tests.py +++ b/qpid/cpp/src/tests/cluster_tests.py @@ -29,9 +29,19 @@ from itertools import chain log = getLogger("qpid.cluster_tests") +# Note: brokers that shut themselves down due to critical error during +# normal operation will still have an exit code of 0. Brokers that +# shut down because of an error found during initialize will exit with +# a non-0 code. Hence the apparently inconsistent use of EXPECT_EXIT_OK +# and EXPECT_EXIT_FAIL in some of the tests below. + +# FIXME aconway 2010-03-11: resolve this - ideally any exit due to an error +# should give non-0 exit status. + # Import scripts as modules qpid_cluster=import_script(checkenv("QPID_CLUSTER_EXEC")) + def readfile(filename): """Returns te content of file named filename as a string""" f = file(filename) @@ -144,7 +154,7 @@ class LongTests(BrokerTest): i += 1 b = cluster.start(expect=EXPECT_EXIT_FAIL) ErrorGenerator(b) - time.sleep(1) + time.sleep(min(5,self.duration()/2)) sender.stop() receiver.stop(sender.sent) for i in range(i, len(cluster)): cluster[i].kill() @@ -152,7 +162,7 @@ class LongTests(BrokerTest): def test_management(self): """Run management clients and other clients concurrently.""" - # FIXME aconway 2010-03-03: move to framework + # TODO aconway 2010-03-03: move to brokertest framework class ClientLoop(StoppableThread): """Run a client executable in a loop.""" def __init__(self, broker, cmd): @@ -173,14 +183,21 @@ class LongTests(BrokerTest): self.cmd, expect=EXPECT_UNKNOWN) finally: self.lock.release() try: exit = self.process.wait() - except: exit = 1 + 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 + 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 status %s" % - (self.broker.name, exit)) + self.process.unexpected( + "client of %s exit code %s"%(self.broker.name, exit)) finally: self.lock.release() except Exception, e: self.error = RethrownException("Error in ClientLoop.run") @@ -218,9 +235,7 @@ class LongTests(BrokerTest): ["perftest", "--count", 1000, "--base-name", str(qpid.datatypes.uuid4()), "--port", broker.port()], ["qpid-queue-stats", "-a", "localhost:%s" %(broker.port())], - [os.path.join(self.rootdir, "testagent/testagent"), "localhost", - str(broker.port())] - ]: + ["testagent", "localhost", str(broker.port())] ]: batch.append(ClientLoop(broker, cmd)) clients.append(batch) @@ -238,7 +253,7 @@ class LongTests(BrokerTest): start_mclients(b) while time.time() < endtime: - time.sleep(min(5,self.duration())) + time.sleep(min(5,self.duration()/2)) for b in cluster[alive:]: b.ready() # Check if a broker crashed. # Kill the first broker. Ignore errors on its clients and all the mclients for c in clients[alive] + mclients: c.expect_fail() @@ -252,7 +267,6 @@ class LongTests(BrokerTest): b = cluster.start() start_clients(b) for b in cluster[alive:]: start_mclients(b) - for c in chain(mclients, *clients): c.stop() @@ -283,6 +297,11 @@ class StoreTests(BrokerTest): m = cluster.start("restartme").get_message("q") self.assertEqual("x", m.content) + def stop_cluster(self,broker): + """Clean shut-down of a cluster""" + self.assertEqual(0, qpid_cluster.main( + ["qpid-cluster", "-kf", broker.host_port()])) + def test_persistent_restart(self): """Verify persistent cluster shutdown/restart scenarios""" cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"]) @@ -298,7 +317,7 @@ class StoreTests(BrokerTest): self.assertEqual(c.get_message("q").content, "2") # Shut down the entire cluster cleanly and bring it back up a.send_message("q", Message("3", durable=True)) - self.assertEqual(0, qpid_cluster.main(["qpid-cluster", "-kf", a.host_port()])) + self.stop_cluster(a) a = cluster.start("a", wait=False) b = cluster.start("b", wait=False) c = cluster.start("c", wait=True) @@ -316,7 +335,7 @@ class StoreTests(BrokerTest): b.kill() self.assertEqual(c.get_message("q").content, "4") c.send_message("q", Message("clean", durable=True)) - self.assertEqual(0, qpid_cluster.main(["qpid-cluster", "-kf", c.host_port()])) + self.stop_cluster(c) a = cluster.start("a", wait=False) b = cluster.start("b", wait=False) c = cluster.start("c", wait=True) @@ -329,7 +348,7 @@ class StoreTests(BrokerTest): a.terminate() cluster2 = self.cluster(1, args=self.args()) try: - a = cluster2.start("a", expect=EXPECT_EXIT_OK) + a = cluster2.start("a", expect=EXPECT_EXIT_FAIL) a.ready() self.fail("Expected exception") except: pass @@ -339,27 +358,29 @@ class StoreTests(BrokerTest): cluster = self.cluster(0, args=self.args()+["--cluster-size=2"]) a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False) b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False) - self.assertEqual(0, qpid_cluster.main(["qpid_cluster", "-kf", a.host_port()])) + self.stop_cluster(a) self.assertEqual(a.wait(), 0) self.assertEqual(b.wait(), 0) # Restart with a different member and shut down. a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False) c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=False) - self.assertEqual(0, qpid_cluster.main(["qpid_cluster", "-kf", a.host_port()])) + self.stop_cluster(a) self.assertEqual(a.wait(), 0) self.assertEqual(c.wait(), 0) - # Mix members from both shutdown events, they should fail - a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False) - b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False) + # FIXME aconway 2010-03-11: can't predict the exit status of these + # as it depends on the order of delivery of initial-status messages. + # See comment at top of this file. + a = cluster.start("a", expect=EXPECT_UNKNOWN, wait=False) + b = cluster.start("b", expect=EXPECT_UNKNOWN, wait=False) self.assertRaises(Exception, lambda: a.ready()) self.assertRaises(Exception, lambda: b.ready()) def assert_dirty_store(self, broker): - self.assertRaises(Exception, lambda: broker.ready()) + assert retry(lambda: os.path.exists(broker.log)), "Missing log file %s"%broker.log msg = re.compile("critical.*no clean store") - assert msg.search(readfile(broker.log)) + assert retry(lambda: msg.search(readfile(broker.log))), "Expected dirty store message in %s"%broker.log def test_solo_store_clean(self): # A single node cluster should always leave a clean store. @@ -371,7 +392,6 @@ class StoreTests(BrokerTest): self.assertEqual(a.get_message("q").content, "x") def test_last_store_clean(self): - # Verify that only the last node in a cluster to shut down has # a clean store. Start with cluster of 3, reduce to 1 then # increase again to ensure that a node that was once alone but @@ -390,13 +410,41 @@ class StoreTests(BrokerTest): time.sleep(0.1) # pause for a to find out hes last. a.kill() # really last # b & c should be dirty - b = cluster.start("b", wait=False, expect=EXPECT_EXIT_OK) + b = cluster.start("b", wait=False, expect=EXPECT_EXIT_FAIL) self.assert_dirty_store(b) - c = cluster.start("c", wait=False, expect=EXPECT_EXIT_OK) + c = cluster.start("c", wait=False, expect=EXPECT_EXIT_FAIL) self.assert_dirty_store(c) # a should be clean a = cluster.start("a") self.assertEqual(a.get_message("q").content, "x") + def test_restart_clean(self): + """Verify that we can re-start brokers one by one in a + persistent cluster after a clean oshutdown""" + cluster = self.cluster(0, self.args()) + a = cluster.start("a", expect=EXPECT_EXIT_OK) + b = cluster.start("b", expect=EXPECT_EXIT_OK) + c = cluster.start("c", expect=EXPECT_EXIT_OK) + a.send_message("q", Message("x", durable=True)) + self.stop_cluster(a) + a = cluster.start("a") + b = cluster.start("b") + c = cluster.start("c") + self.assertEqual(c.get_message("q").content, "x") + def test_join_sub_size(self): + """Verify that after starting a cluster with cluster-size=N, + we can join new members even if size < N-1""" + cluster = self.cluster(0, self.args()) + a = cluster.start("a", wait=False, expect=EXPECT_EXIT_FAIL) + b = cluster.start("b", wait=False, expect=EXPECT_EXIT_FAIL) + c = cluster.start("c") + a.send_message("q", Message("x", durable=True)) + a.send_message("q", Message("y", durable=True)) + a.kill() + b.kill() + a = cluster.start("a") + self.assertEqual(c.get_message("q").content, "x") + b = cluster.start("b") + self.assertEqual(c.get_message("q").content, "y") diff --git a/qpid/cpp/src/tests/qpid_recv.cpp b/qpid/cpp/src/tests/qpid_recv.cpp index 9e4e202053..10738578ed 100644 --- a/qpid/cpp/src/tests/qpid_recv.cpp +++ b/qpid/cpp/src/tests/qpid_recv.cpp @@ -27,12 +27,14 @@ #include <qpid/Options.h> #include <qpid/log/Logger.h> #include <qpid/log/Options.h> +#include <qpid/client/amqp0_10/FailoverUpdates.h> #include "TestOptions.h" #include <iostream> - +#include <memory> using namespace qpid::messaging; +using qpid::client::amqp0_10::FailoverUpdates; using namespace std; @@ -54,6 +56,7 @@ struct Options : public qpid::Options uint tx; uint rollbackFrequency; bool printHeaders; + bool failoverUpdates; qpid::log::Options log; Options(const std::string& argv0=std::string()) @@ -69,6 +72,7 @@ struct Options : public qpid::Options tx(0), rollbackFrequency(0), printHeaders(false), + failoverUpdates(false), log(argv0) { addOptions() @@ -84,6 +88,7 @@ struct Options : public qpid::Options ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)") ("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)") ("print-headers", qpid::optValue(printHeaders), "If specified print out all message headers as well as content") + ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover") ("help", qpid::optValue(help), "print this usage statement"); add(log); } @@ -143,9 +148,10 @@ int main(int argc, char ** argv) { Options opts; if (opts.parse(argc, argv)) { + Connection connection(opts.connectionOptions); try { - Connection connection(opts.connectionOptions); connection.open(opts.url); + std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0); Session session = connection.newSession(opts.tx > 0); Receiver receiver = session.createReceiver(opts.address); receiver.setCapacity(opts.capacity); @@ -201,6 +207,7 @@ int main(int argc, char ** argv) return 0; } catch(const std::exception& error) { std::cerr << "Failure: " << error.what() << std::endl; + connection.close(); } } return 1; diff --git a/qpid/cpp/src/tests/qpid_send.cpp b/qpid/cpp/src/tests/qpid_send.cpp index 57c348ab9c..a8b0241a1d 100644 --- a/qpid/cpp/src/tests/qpid_send.cpp +++ b/qpid/cpp/src/tests/qpid_send.cpp @@ -25,16 +25,15 @@ #include <qpid/messaging/Message.h> #include <qpid/messaging/Sender.h> #include <qpid/messaging/Session.h> +#include <qpid/client/amqp0_10/FailoverUpdates.h> #include "TestOptions.h" #include <fstream> #include <iostream> +#include <memory> using namespace qpid::messaging; -using qpid::framing::Uuid; -using qpid::sys::AbsTime; -using qpid::sys::now; -using qpid::sys::TIME_INFINITE; +using qpid::client::amqp0_10::FailoverUpdates; typedef std::vector<std::string> string_vector; @@ -49,7 +48,6 @@ struct Options : public qpid::Options std::string url; std::string connectionOptions; std::string address; - int64_t timeout; uint count; std::string id; std::string replyto; @@ -64,13 +62,13 @@ struct Options : public qpid::Options uint tx; uint rollbackFrequency; uint capacity; + bool failoverUpdates; qpid::log::Options log; Options(const std::string& argv0=std::string()) : qpid::Options("Options"), help(false), url("amqp:tcp:127.0.0.1"), - timeout(TIME_INFINITE), count(1), sendEos(0), durable(false), @@ -78,13 +76,13 @@ struct Options : public qpid::Options tx(0), rollbackFrequency(0), capacity(0), + failoverUpdates(false), log(argv0) { addOptions() ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to") ("address,a", qpid::optValue(address, "ADDRESS"), "address to drain from") ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection") - ("timeout,t", qpid::optValue(timeout, "TIMEOUT"), "exit after the specified time") ("count,c", qpid::optValue(count, "COUNT"), "stop after count messages have been sent, zero disables") ("id,i", qpid::optValue(id, "ID"), "use the supplied id instead of generating one") ("reply-to", qpid::optValue(replyto, "REPLY-TO"), "specify reply-to address") @@ -99,6 +97,7 @@ struct Options : public qpid::Options ("capacity", qpid::optValue(capacity, "N"), "size of the senders outgoing message queue") ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)") ("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)") + ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover") ("help", qpid::optValue(help), "print this usage statement"); add(log); } @@ -182,9 +181,10 @@ int main(int argc, char ** argv) { Options opts; if (opts.parse(argc, argv)) { + Connection connection(opts.connectionOptions); try { - Connection connection(opts.connectionOptions); connection.open(opts.url); + std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0); Session session = connection.newSession(opts.tx > 0); Sender sender = session.createSender(opts.address); if (opts.capacity) sender.setCapacity(opts.capacity); @@ -230,6 +230,7 @@ int main(int argc, char ** argv) return 0; } catch(const std::exception& error) { std::cout << "Failed: " << error.what() << std::endl; + connection.close(); } } return 1; diff --git a/qpid/cpp/src/tests/qpid_stream.cpp b/qpid/cpp/src/tests/qpid_stream.cpp index ca21fa248b..5ed7f84492 100644 --- a/qpid/cpp/src/tests/qpid_stream.cpp +++ b/qpid/cpp/src/tests/qpid_stream.cpp @@ -40,16 +40,33 @@ struct Args : public qpid::Options { std::string url; std::string address; + uint size; uint rate; bool durable; - - Args() : url("amqp:tcp:127.0.0.1:5672"), address("test-queue"), rate(1000), durable(false) + uint receiverCapacity; + uint senderCapacity; + uint ackFrequency; + + Args() : + url("amqp:tcp:127.0.0.1:5672"), + address("test-queue"), + size(512), + rate(1000), + durable(false), + receiverCapacity(0), + senderCapacity(0), + ackFrequency(1) { addOptions() ("url", qpid::optValue(url, "URL"), "Url to connect to.") ("address", qpid::optValue(address, "ADDRESS"), "Address to stream messages through.") + ("size", qpid::optValue(size, "bytes"), "Message size in bytes (content only, not headers).") ("rate", qpid::optValue(rate, "msgs/sec"), "Rate at which to stream messages.") - ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable."); + ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.") + ("sender-capacity", qpid::optValue(senderCapacity, "N"), "Credit window (0 implies infinite window)") + ("receiver-capacity", qpid::optValue(receiverCapacity, "N"), "Credit window (0 implies infinite window)") + ("ack-frequency", qpid::optValue(ackFrequency, "N"), + "Ack frequency (0 implies none of the messages will get accepted)"); } }; @@ -70,8 +87,8 @@ struct Client : qpid::sys::Runnable void run() { + Connection connection; try { - Connection connection; connection.open(opts.url); Session session = connection.newSession(); doWork(session); @@ -79,6 +96,7 @@ struct Client : qpid::sys::Runnable connection.close(); } catch(const std::exception& error) { std::cout << error.what() << std::endl; + connection.close(); } } @@ -93,7 +111,8 @@ struct Publish : Client void doWork(Session& session) { Sender sender = session.createSender(opts.address); - Message msg; + if (opts.senderCapacity) sender.setCapacity(opts.senderCapacity); + Message msg(std::string(opts.size, 'X')); uint64_t interval = qpid::sys::TIME_SEC / opts.rate; uint64_t sent = 0, missedRate = 0; qpid::sys::AbsTime start = qpid::sys::now(); @@ -123,9 +142,12 @@ struct Consume : Client double maxLatency = 0; double totalLatency = 0; Receiver receiver = session.createReceiver(opts.address); + if (opts.receiverCapacity) receiver.setCapacity(opts.receiverCapacity); while (receiver.fetch(msg)) { - session.acknowledge();//TODO: add batching option ++received; + if (opts.ackFrequency && (received % opts.ackFrequency == 0)) { + session.acknowledge(); + } //calculate latency uint64_t receivedAt = timestamp(qpid::sys::now()); uint64_t sentAt = msg.getHeaders()[TIMESTAMP].asUint64(); diff --git a/qpid/cpp/src/tests/quick_topictest.ps1 b/qpid/cpp/src/tests/quick_topictest.ps1 index b2efbdd684..8f5b2caff7 100644 --- a/qpid/cpp/src/tests/quick_topictest.ps1 +++ b/qpid/cpp/src/tests/quick_topictest.ps1 @@ -20,11 +20,11 @@ # Quick and quiet topic test for make check. [string]$me = $myInvocation.InvocationName $srcdir = Split-Path $me -& "$srcdir\topictest.ps1" -subscribers 2 -messages 2 -batches 1 > topictest.log 2>&1 +Invoke-Expression "$srcdir\topictest.ps1 -subscribers 2 -messages 2 -batches 1" > topictest.log 2>&1 if (!$?) { "$me FAILED:" cat topictest.log - exit $LastExitCode + exit 1 } Remove-Item topictest.log exit 0 diff --git a/qpid/cpp/src/tests/testagent/testagent.cpp b/qpid/cpp/src/tests/testagent.cpp index 61b47d83da..61b47d83da 100644 --- a/qpid/cpp/src/tests/testagent/testagent.cpp +++ b/qpid/cpp/src/tests/testagent.cpp diff --git a/qpid/cpp/src/tests/testagent.mk b/qpid/cpp/src/tests/testagent.mk new file mode 100644 index 0000000000..f5a84b07ff --- /dev/null +++ b/qpid/cpp/src/tests/testagent.mk @@ -0,0 +1,51 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Build a simple qmf agent for test purposes. + +QMF_GEN=$(top_srcdir)/managementgen/qmf-gen + +TESTAGENT_GEN_SRC= \ + testagent_gen/qmf/org/apache/qpid/agent/example/Parent.h \ + testagent_gen/qmf/org/apache/qpid/agent/example/Child.h \ + testagent_gen/qmf/org/apache/qpid/agent/example/Parent.cpp \ + testagent_gen/qmf/org/apache/qpid/agent/example/Child.cpp \ + testagent_gen/qmf/org/apache/qpid/agent/example/ArgsParentCreate_child.h \ + testagent_gen/qmf/org/apache/qpid/agent/example/EventChildCreated.h \ + testagent_gen/qmf/org/apache/qpid/agent/example/EventChildDestroyed.h \ + testagent_gen/qmf/org/apache/qpid/agent/example/EventChildCreated.cpp \ + testagent_gen/qmf/org/apache/qpid/agent/example/EventChildDestroyed.cpp \ + testagent_gen/qmf/org/apache/qpid/agent/example/Package.h \ + testagent_gen/qmf/org/apache/qpid/agent/example/Package.cpp + +$(TESTAGENT_GEN_SRC): testagent_gen.timestamp + +testagent_gen.timestamp: testagent.xml + $(QMF_GEN) -o testagent_gen/qmf $(srcdir)/testagent.xml + touch testagent_gen.timestamp + +CLEANFILES+=$(TESTAGENT_GEN_SRC) testagent_gen.timestamp + +testagent-testagent.$(OBJEXT): $(TESTAGENT_GEN_SRC) +qpidtest_PROGRAMS+=testagent +testagent_CXXFLAGS=$(CXXFLAGS) -Itestagent_gen +testagent_SOURCES=testagent.cpp $(TESTAGENT_GEN_SRC) +testagent_LDADD=$(top_builddir)/src/libqmf.la + +EXTRA_DIST+=testagent.xml diff --git a/qpid/cpp/src/tests/testagent/schema.xml b/qpid/cpp/src/tests/testagent.xml index 8be21b7e68..8be21b7e68 100644 --- a/qpid/cpp/src/tests/testagent/schema.xml +++ b/qpid/cpp/src/tests/testagent.xml diff --git a/qpid/cpp/src/tests/testagent/Makefile.am b/qpid/cpp/src/tests/testagent/Makefile.am deleted file mode 100644 index 7ff7cb0bd7..0000000000 --- a/qpid/cpp/src/tests/testagent/Makefile.am +++ /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. -# - -# Build a simple qmf agent for test purposes. - -QMF_GEN=$(top_srcdir)/managementgen/qmf-gen -GEN_SRC= \ - gen/qmf/org/apache/qpid/agent/example/Parent.h \ - gen/qmf/org/apache/qpid/agent/example/Child.h \ - gen/qmf/org/apache/qpid/agent/example/Parent.cpp \ - gen/qmf/org/apache/qpid/agent/example/Child.cpp \ - gen/qmf/org/apache/qpid/agent/example/ArgsParentCreate_child.h \ - gen/qmf/org/apache/qpid/agent/example/EventChildCreated.h \ - gen/qmf/org/apache/qpid/agent/example/EventChildDestroyed.h \ - gen/qmf/org/apache/qpid/agent/example/EventChildCreated.cpp \ - gen/qmf/org/apache/qpid/agent/example/EventChildDestroyed.cpp \ - gen/qmf/org/apache/qpid/agent/example/Package.h \ - gen/qmf/org/apache/qpid/agent/example/Package.cpp - -$(GEN_SRC): gen.timestamp - -gen.timestamp: schema.xml - $(QMF_GEN) -o gen/qmf $(srcdir)/schema.xml - touch gen.timestamp - -CLEANFILES=$(GEN_SRC) - -INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src -Igen - -noinst_PROGRAMS=testagent -testagent_SOURCES=testagent.cpp $(GEN_SRC) -testagent_LDADD=$(top_builddir)/src/libqmf.la - -EXTRA_DIST=schema.xml diff --git a/qpid/cpp/src/tests/topictest.ps1 b/qpid/cpp/src/tests/topictest.ps1 index 0d22cea657..59a483c2d5 100644 --- a/qpid/cpp/src/tests/topictest.ps1 +++ b/qpid/cpp/src/tests/topictest.ps1 @@ -17,19 +17,21 @@ # under the License. # -# Run the C++ topic test -$srcdir = Split-Path $myInvocation.InvocationName - # Parameters with default values: s (subscribers) m (messages) b (batches) # h (host) t (false; use transactions) param ( [int]$subscribers = 10, - [int]$messages = 2000, + [int]$message_count = 2000, [int]$batches = 10, [string]$broker, [switch] $t # transactional ) +# Run the C++ topic test +[string]$me = $myInvocation.InvocationName +$srcdir = Split-Path $me +#$srcdir = Split-Path $myInvocation.InvocationName + # Clean up old log files Get-Item subscriber_*.log | Remove-Item @@ -41,7 +43,7 @@ if ($t) { . $srcdir\find_prog.ps1 .\topic_listener.exe function subscribe { - param ([int]$num) + param ([int]$num, [string]$sub) "Start subscriber $num" $LOG = "subscriber_$num.log" $cmdline = ".\$sub\topic_listener $transactional > $LOG 2>&1 @@ -51,7 +53,8 @@ function subscribe { } function publish { - Invoke-Expression ".\$sub\topic_publisher --messages $messages --batches $batches --subscribers $subscribers $host $transactional" 2>&1 + param ([string]$sub) + Invoke-Expression ".\$sub\topic_publisher --messages $message_count --batches $batches --subscribers $subscribers $host $transactional" 2>&1 } if ($broker.length) { @@ -60,11 +63,11 @@ if ($broker.length) { $i = $subscribers while ($i -gt 0) { - subscribe $i + subscribe $i $sub $i-- } # FIXME aconway 2007-03-27: Hack around startup race. Fix topic test. Start-Sleep 2 -publish +publish $sub exit $LastExitCode diff --git a/qpid/cpp/xml/cluster.xml b/qpid/cpp/xml/cluster.xml index 44f055ea32..8f94eb3fd1 100644 --- a/qpid/cpp/xml/cluster.xml +++ b/qpid/cpp/xml/cluster.xml @@ -64,7 +64,6 @@ <field name="cluster-id" type="uuid"/>> <field name="store-state" type="store-state"/> <field name="shutdown-id" type="uuid"/> - <field name="config-seq" type="sequence-no"/> <field name="first-config" type="str16"/> </control> @@ -122,6 +121,10 @@ encryption (e.g. ssl), ssf is the bit length of the key. Zero if no encryption provided. --> <field name="ssf" type="uint32"/> + <!-- external auth id (e.g. ssl client certificate id) --> + <field name="authid" type="str16"/> + <!-- exclude certain sasl mechs, used with ssl and sasl-external --> + <field name="nodict" type="bit"/> </control> <!-- Marks the cluster-wide point when a connection is considered closed. --> diff --git a/qpid/doc/book/Makefile b/qpid/doc/book/Makefile new file mode 100644 index 0000000000..2781f131ab --- /dev/null +++ b/qpid/doc/book/Makefile @@ -0,0 +1,42 @@ +############################################################### +# +# This Makefile requires the following: +# +# Apache FOP, version 0.95 or higher +# Docbook 4.5 +# Docbook XSL stylesheets - tested with xsl-stylesheets-1.75.2 +# xsltproc +# xmllint +# + +DOCBOOKXSL = /usr/share/sgml/docbook/xsl-stylesheets-1.75.2/ + +out/pdf/qpid-book.pdf: build/qpid-book.fo + mkdir -p out/pdf + fop build/qpid-book.fo out/pdf/qpid-book.pdf + +build/qpid-book.fo: build/qpid-book.xml + xsltproc ${DOCBOOKXSL}/fo/docbook.xsl build/qpid-book.xml >build/qpid-book.fo + +build/qpid-book.xml: + mkdir -p build + xmllint --xinclude src/Book.xml >build/qpid-book.xml + +out/html/index.html: build/qpid-book.xml + mkdir -p out/html + mkdir -p out/html/Common_Content + cd out/html; ln -s ../../src/images + cd out/html/Common_Content; ln -s ../../../src/images + cd out/html; xsltproc ${DOCBOOKXSL}/html/chunk.xsl ../../build/qpid-book.xml + +pdf: out/pdf/qpid-book.pdf + +html: out/html/index.html + +all: pdf html + +clean: + rm -rf build + rm -rf out + +
\ No newline at end of file diff --git a/qpid/doc/book/README.txt b/qpid/doc/book/README.txt new file mode 100644 index 0000000000..2deb20a13b --- /dev/null +++ b/qpid/doc/book/README.txt @@ -0,0 +1,118 @@ +The documentation in this directory is written in DocBook 4.5. The +original content was taken from the Apache Qpid Wiki. + +1. Building the Documentation + +You need the following to build the documentation: + +- Apache FOP, version 0.95 or higher +- Docbook 4.5 +- Docbook XSL stylesheets - I have tested with xsl-stylesheets-1.75.2 +- xsltproc +- xmllint + +On many Linux machines, these can usually be installed from standard +repos. For instance, on Fedora they can be installed as follows: + +$ sudo yum install fop docbook-dtds docbook-style-xsl libxslt libxml2 + +After installing, use make to build the documentation: + +$ make + +By default, the Makefile builds a PDF. It supports the following +targets: + +pdf Make the PDF +html Make HTML pages +all Make both PDF and HTML +clean Delete the build and output directories + + +You will see quite a few error messages. Many of these are due to +unresolved links, and these should go away. Many are due to the +verbosity of Apache FOP, which generates many warnings. + +2. Editing Tools + +For Emacs, I like nxml-mode, especially if you learn how to use tag +completion, outlining, etc. This is described in some detail in +http://www.dpawson.co.uk/relaxng/nxml/info.html. + +For vi, the macros described in this Linux Journal article may be +helpful: http://www.linuxjournal.com/article/7737. + +Commercial XML editors provide good support for DocBook. On Windows, I +like Stylus Studio (http://www.stylusstudio.com/). On Linux, I like +Oxygen (http://www.oxygenxml.com/). + +Here's a page on authoring tools for DocBook: +http://wiki.docbook.org/topic/DocBookAuthoringTools + + +3. File Structure + +The source files are in qpid/doc/book/src. + +The following XInclude tree shows the organization of files in the +document. + +Book.xml + Book-Info.xml + Introduction.xml + AMQP.xml + Getting-Started.xml + Download.xml + AMQP-Messaging-Broker-CPP.xml + Running-CPP-Broker.xml + Cheat-Sheet-for-configuring-Queue-Options.xml + Cheat-Sheet-for-configuring-Exchange-Options.xml + Using-Broker-Federation.xml + SSL.xml + LVQ.xml + queue-state-replication.xml + Starting-a-cluster.xml + ACL.xml + Managing-CPP-Broker.xml + QMan-Qpid-Management-bridge.xml + Qpid-Management-Framework.xml + Management-Design-notes.xml + QMF-Python-Console-Tutorial.xml + AMQP-Messaging-Broker-Java.xml + Java-Broker-Feature-Guide.xml + Qpid-Java-FAQ.xml + Java-Environment-Variables.xml + Qpid-Troubleshooting-Guide.xml + Add-New-Users.xml + Configure-ACLs.xml + Configure-Java-Qpid-to-use-a-SSL-connection.xml + Configure-Log4j-CompositeRolling-Appender.xml + Configure-the-Broker-via-config.xml.xml + Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml + Debug-using-log4j.xml + How-to-Tune-M3-Java-Broker-Performance.xml + Qpid-Java-Build-How-To.xml + Use-Priority-Queues.xml + Qpid-JMX-Management-Console.xml + Configuring-Management-Users.xml + Configuring-Qpid-JMX-Management-Console.xml + Management-Console-Security.xml + Qpid-JMX-Management-Console-FAQ.xml + Qpid-JMX-Management-Console-User-Guide.xml + Qpid-Management-Features.xml + MessageStore-Tool.xml + Qpid-Java-Broker-Management-CLI.xml + AMQP-Java-JMS-Messaging-Client.xml + System-Properties.xml + Connection-URL-Format.xml + Binding-URL-Format.xml + AMQP-C++-Messaging-Client.xml + AMQP-.NET-Messaging-Client.xml + NET-User-Guide.xml + Excel-AddIn.xml + WCF.xml + AMQP-Python-Messaging-Client.xml + PythonBrokerTest.xml + AMQP-Ruby-Messaging-Client.xml + AMQP-Compatibility.xml + Qpid-Interoperability-Documentation.xml diff --git a/qpid/doc/book/build.sh b/qpid/doc/book/build.sh new file mode 100755 index 0000000000..fd78ef79e8 --- /dev/null +++ b/qpid/doc/book/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash -ex + +######################################################################## +# +# Build a PDF from Docbook XML +# +# The Makefile is cleaner .... +# +######################################################################## + +rm -rf build +mkdir -p build +mkdir -p pdf + +# Assemble all documents using XInclude +xmllint --xinclude src/Book.xml >build/qpid-book.xml + +# Create the .fo +xsltproc /usr/share/sgml/docbook/xsl-stylesheets-1.75.2/fo/docbook.xsl build/qpid-book.xml >build/qpid-book.fo + +# Use Apache FOP to create the PDF +fop build/qpid-book.fo pdf/qpid-book.pdf diff --git a/qpid/doc/book/build.xml b/qpid/doc/book/build.xml new file mode 100644 index 0000000000..ff6c058f9f --- /dev/null +++ b/qpid/doc/book/build.xml @@ -0,0 +1,173 @@ +<!-- + Build Apache Qpid documentation. + + For documentation on using XSLT in ant, see http://ant.apache.org/manual/CoreTasks/style.html + + For documentation on using Apache FOP in ant, see http://xmlgraphics.apache.org/fop/0.94/anttask.html + + Note: Validation is currently off by default, too many dangling references. We will tighten this up as soon as we can. + --> + +<project + name="generate" + basedir="." + default="pdf"> + + <property name="Qpid" value="http://qpid.apache.org"/> + + +<!-- +########################################################################### +# +# Directories +# +# Change the directory paths in this section to the correct paths for +# your machine. +# +########################################################################### +--> + + <property name="src.dir" location="src"/> + <property name="build.dir" location="build"/> + <property name="out.dir" location="out"/> + + <!-- Docbook schemas and stylesheets --> + <property name="schema.dir" location="docbook"/> + <property name="style.dir" location="docbook-xsl"/> + <property name="fo.stylesheet" location="${style.dir}/fo/docbook.xsl" /> + <property name="html.stylesheet" location="${style.dir}/html/docbook.xsl" /> + + <!-- ${lib.dir} has subdirectories for saxon and fop --> + <property name="lib.dir" location="lib"/> + + <property name="xmllint" location="/usr/bin/xmllint"/> + +<!-- +########################################################################### +# +# Setting up tasks +# +# You shouldn't need to change anything in this section or following sections. +# +########################################################################### +--> + + <path id="saxon6.classpath"> + <pathelement location="${lib.dir}/saxon/resolver.jar"/> + <pathelement location="${lib.dir}/saxon/xml-apis.jar"/> + <pathelement location="${lib.dir}/saxon/xercesImpl.jar"/> + <pathelement location="${lib.dir}/saxon/saxon.jar"/> + </path> + +<taskdef name="fop" + classname="org.apache.fop.tools.anttasks.Fop"> + <classpath> + <fileset dir="${lib.dir}/fop-0.95/lib"> + <include name="*.jar"/> + </fileset> + <fileset dir="${lib.dir}/fop-0.95/build"> + <include name="fop.jar"/> + <include name="fop-hyph.jar" /> + </fileset> + </classpath> +</taskdef> + +<!-- +########################################################################### +# +# Tasks +# +########################################################################### +--> + +<!-- + init +--> + +<target name="init"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${out.dir}"/> +</target> + +<!-- + XInclude +--> + +<target name="xinclude" depends="init"> + <exec executable="${xmllint}"> + <arg value="-o"/> + <arg value="${build.dir}/xinclude.xml"/> + <arg value="--xinclude"/> + <arg value="${src.dir}/Book.xml"/> + </exec> +</target> + + + +<!-- + FO +--> + + <target name="fo" depends="xinclude" description="Generates qpid-book.fo, which is needed to create a PDF"> + + <xslt in="${build.dir}/xinclude.xml" out="${build.dir}/qpid-book.fo" + style="${fo.stylesheet}" classpathref="saxon6.classpath"> + <param name="specdoc" expression="${spec.code}"/> + <param name="uri" expression="${spec.uri}"/> + </xslt> + </target> + + +<!-- + PDF +--> + +<target name="pdf" depends="fo" description="Generates qpid-book.pdf"> + <fop format="application/pdf" + fofile="${build.dir}/qpid-book.fo" + outfile="${out.dir}/qpid-book.pdf"/> +</target +> +<!-- + HTML +--> + + <target name="html" depends="xinclude" description="Generates qpid-book.html"> + <xslt in="${build.dir}/xinclude.xml" out="${out.dir}/qpid-book.html" + style="${html.stylesheet}" classpathref="saxon6.classpath"> + </xslt> + </target> + +<!-- + Validate +--> + +<target name="validate" depends="xinclude"> + <xmlvalidate file="${build.dir}/xinclude.xml" warn="true"> + <dtd publicId="-//OASIS//DTD DocBook V4.5//EN" + location="docbook/docbook.dtd"/> + </xmlvalidate> +</target> + +<!-- + Clean +--> + +<target name="clean"> + <delete dir="${build.dir}"/> +</target> + +<!-- + Check +--> + +<target name="check" depends="xinclude"> + <xmlvalidate file="${build.dir}/xinclude.xml" warn="false"> + <dtd publicId="-//OASIS//DTD DocBook V4.5//EN" + location="docbook/docbook.dtd"/> + </xmlvalidate> +</target> + + +</project> + diff --git a/qpid/doc/book/src/SASL-Compatibility.xml b/qpid/doc/book/src/SASL-Compatibility.xml index 96c523e519..beb138c182 100644 --- a/qpid/doc/book/src/SASL-Compatibility.xml +++ b/qpid/doc/book/src/SASL-Compatibility.xml @@ -12,18 +12,18 @@ This table list the various SASL mechanisms that each component supports. The ve functionality was added to the product. || Component || ANONYMOUS || CRAM-MD5 || DIGEST-MD5 || EXTERNAL || GSSAPI/Kerberos || PLAIN || -| C++ Broker |M3\[[#1]\] |M3\[[#1],[#2]\]| | |M3\[[#1],[#2]\] | M1 | -| C++ Client |M3\[[#1]\] | | | | | M1 | +| C++ Broker |M3 |M3 | M3 |0.8\[[#1]\]|M3 | M1 | +| C++ Client |M3 |0.5 | 0.5 |0.8\[[#1]\]| | M1 | | Java Broker | | M1 | | | | M1 | -| Java Client | | M1 | | | | M1 | +| Java Client | | M1 | | M1 | | M1 | | .Net Client | M2 | M2 | M2 | M2 | | M2 | -| Python Client | | | | | | ? | -| Ruby Client | | | | | | ? | +| Python Client |0.6\[[#2]\]|0.6\[[#2]\]|0.6\[[#2]\] |0.6\[[#2]\]|0.6\[[#2]\] | M4 | +| Ruby Client |0.6\[[#2]\]|0.6\[[#2]\]|0.6\[[#2]\] |0.6\[[#2]\]|0.6\[[#2]\] | M4 | {anchor:1} -1: Support for these will be in M3 (currently available on trunk). +1: Only enabled for client authenticated SSL connections. {anchor:2} -2: C++ Broker uses [Cyrus Sasl|http://freshmeat.net/projects/cyrussasl/] which supports CRAM-MD5 and GSSAPI but these have not been tested yet +2: On linux only via cyrus-sasl integration. h4. Custom Mechanisms diff --git a/qpid/doc/book/src/schemas.xml b/qpid/doc/book/src/schemas.xml index b78cdd5d3c..550481ca0a 100644 --- a/qpid/doc/book/src/schemas.xml +++ b/qpid/doc/book/src/schemas.xml @@ -21,37 +21,73 @@ --> <locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0"> - <uri resource="Download.xml" typeId="DocBook"/> - <uri resource="Getting-Started.xml" typeId="DocBook"/> - <uri resource="WCF.xml" typeId="DocBook"/> - <uri resource="Book-Info.xml" typeId="DocBook"/> - <uri resource="Book-Info.xml" typeId="DocBook"/> - <uri resource="Book.xml" uri="../../../../../../../usr/share/xml/docbook5/schema/rng/5.0/docbookxi.rnc"/> - <uri resource="queue%20state%20replication.xml" typeId="DocBook"/> - <uri resource="queue state replication.xml" typeId="DocBook"/> - <uri resource="AMQP Ruby Messaging Client.xml" typeId="DocBook"/> - <uri resource="AMQP Compatibility.xml" typeId="DocBook"/> - <uri resource="Qpid Troubleshooting Guide.xml" typeId="DocBook"/> - <uri resource="Book_Info.xml" typeId="DocBook"/> - <uri resource="Book_Info.xml" typeId="DocBook"/> - <uri resource="AMQP-Messaging-Broker-CPP.xml" typeId="DocBook"/> - <uri resource="Configure the Broker via config.xml.xml" typeId="DocBook"/> - <uri resource="SSL.xml" typeId="DocBook"/> - <uri resource="Using Broker Federation.xml" typeId="DocBook"/> - <uri resource="Cheat Sheet for configuring Exchange Options.xml" typeId="DocBook"/> - <uri resource="foo.xml" typeId="DocBook"/> - <uri resource="Download.xml" typeId="DocBook"/> - <uri resource="Download.xml" typeId="DocBook"/> - <uri resource="Starting a cluster.xml" typeId="DocBook"/> - <uri resource="queue state replication.xml" typeId="DocBook"/> - <uri resource="LVQ.xml" typeId="DocBook"/> - <uri resource="LVQ.xml" typeId="DocBook"/> - <uri resource="Cheat Sheet for configuring Queue Options.xml" typeId="DocBook"/> - <uri resource="Cheat Sheet for configuring Exchange Options.xml" typeId="DocBook"/> - <uri resource="Cheat Sheet for configuring Exchange Options.xml" typeId="DocBook"/> - <uri resource="AMQP-Messaging-Broker-CPP.xml" typeId="DocBook"/> - <uri resource="RASC.xml" typeId="DocBook"/> - <uri resource="Brokers.xml" typeId="DocBook"/> - <uri resource="AMQP.xml" typeId="DocBook"/> - <uri resource="qpid-book.xml" typeId="DocBook"/> + <uri resource="ACL.xml" typeId="DocBook"/> + <uri resource="Add-New-Users.xml" typeId="DocBook"/> + <uri resource="AMQP-C++-Messaging-Client.xml" typeId="DocBook"/> + <uri resource="AMQP-Compatibility.xml" typeId="DocBook"/> + <uri resource="AMQP-Java-JMS-Messaging-Client.xml" typeId="DocBook"/> + <uri resource="AMQP-Messaging-Broker-CPP.xml" typeId="DocBook"/> + <uri resource="AMQP-Messaging-Broker-Java.xml" typeId="DocBook"/> + <uri resource="AMQP-.NET-Messaging-Client.xml" typeId="DocBook"/> + <uri resource="AMQP-Python-Messaging-Client.xml" typeId="DocBook"/> + <uri resource="AMQP-Ruby-Messaging-Client.xml" typeId="DocBook"/> + <uri resource="AMQP.xml" typeId="DocBook"/> + <uri resource="Binding-URL-Format.xml" typeId="DocBook"/> + <uri resource="Book-Info.xml" typeId="DocBook"/> + <uri resource="Book.xml" typeId="DocBook"/> + <uri resource="Broker-CPP.xml" typeId="DocBook"/> + <uri resource="Broker-Java.xml" typeId="DocBook"/> + <uri resource="Cheat-Sheet-for-configuring-Exchange-Options.xml" typeId="DocBook"/> + <uri resource="Cheat-Sheet-for-configuring-Queue-Options.xml" typeId="DocBook"/> + <uri resource="Clients.xml" typeId="DocBook"/> + <uri resource="Configure-ACLs.xml" typeId="DocBook"/> + <uri resource="Configure-Java-Qpid-to-use-a-SSL-connection.xml" typeId="DocBook"/> + <uri resource="Configure-Log4j-CompositeRolling-Appender.xml" typeId="DocBook"/> + <uri resource="Configure-the-Broker-via-config.xml.xml" typeId="DocBook"/> + <uri resource="Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml" typeId="DocBook"/> + <uri resource="Configuring-Management-Users.xml" typeId="DocBook"/> + <uri resource="Configuring-Qpid-JMX-Management-Console.xml" typeId="DocBook"/> + <uri resource="Connection-URL-Format.xml" typeId="DocBook"/> + <uri resource="Debug-using-log4j.xml" typeId="DocBook"/> + <uri resource="Download.xml" typeId="DocBook"/> + <uri resource="Excel-AddIn.xml" typeId="DocBook"/> + <uri resource="FAQ.xml" typeId="DocBook"/> + <uri resource="foo.xml" typeId="DocBook"/> + <uri resource="f.xml" typeId="DocBook"/> + <uri resource="Getting-Started.xml" typeId="DocBook"/> + <uri resource="How-to-Tune-M3-Java-Broker-Performance.xml" typeId="DocBook"/> + <uri resource="How-to-Use-JNDI.xml" typeId="DocBook"/> + <uri resource="Introduction.xml" typeId="DocBook"/> + <uri resource="Java-Broker-Feature-Guide.xml" typeId="DocBook"/> + <uri resource="Java-Environment-Variables.xml" typeId="DocBook"/> + <uri resource="LVQ.xml" typeId="DocBook"/> + <uri resource="Management-Console-Security.xml" typeId="DocBook"/> + <uri resource="Management-Design-notes.xml" typeId="DocBook"/> + <uri resource="Managing-CPP-Broker.xml" typeId="DocBook"/> + <uri resource="MessageStore-Tool.xml" typeId="DocBook"/> + <uri resource="NET-User-Guide.xml" typeId="DocBook"/> + <uri resource="PythonBrokerTest.xml" typeId="DocBook"/> + <uri resource="QMan-Qpid-Management-bridge.xml" typeId="DocBook"/> + <uri resource="QMF-Python-Console-Tutorial.xml" typeId="DocBook"/> + <uri resource="Qpid-ACLs.xml" typeId="DocBook"/> + <uri resource="Qpid-Interoperability-Documentation.xml" typeId="DocBook"/> + <uri resource="Qpid-Java-Broker-Management-CLI.xml" typeId="DocBook"/> + <uri resource="Qpid-Java-Build-How-To.xml" typeId="DocBook"/> + <uri resource="Qpid-Java-FAQ.xml" typeId="DocBook"/> + <uri resource="Qpid-JMX-Management-Console-FAQ.xml" typeId="DocBook"/> + <uri resource="Qpid-JMX-Management-Console-User-Guide.xml" typeId="DocBook"/> + <uri resource="Qpid-JMX-Management-Console.xml" typeId="DocBook"/> + <uri resource="Qpid-Management-Features.xml" typeId="DocBook"/> + <uri resource="Qpid-Management-Framework.xml" typeId="DocBook"/> + <uri resource="Qpid-Troubleshooting-Guide.xml" typeId="DocBook"/> + <uri resource="queue-state-replication.xml" typeId="DocBook"/> + <uri resource="Running-CPP-Broker.xml" typeId="DocBook"/> + <uri resource="SASL-Compatibility.xml" typeId="DocBook"/> + <uri resource="SSL.xml" typeId="DocBook"/> + <uri resource="Starting-a-cluster.xml" typeId="DocBook"/> + <uri resource="System-Properties.xml" typeId="DocBook"/> + <uri resource="Use-Priority-Queues.xml" typeId="DocBook"/> + <uri resource="Using-Broker-Federation.xml" typeId="DocBook"/> + <uri resource="Using-Qpid-with-other-JNDI-Providers.xml" typeId="DocBook"/> + <uri resource="WCF.xml" typeId="DocBook"/> </locatingRules> diff --git a/qpid/dotnet/TestClient/TestClient.csproj b/qpid/dotnet/TestClient/TestClient.csproj index 3b54a27b44..cc7ab37657 100644 --- a/qpid/dotnet/TestClient/TestClient.csproj +++ b/qpid/dotnet/TestClient/TestClient.csproj @@ -1,4 +1,4 @@ -<!-- +<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -19,7 +19,7 @@ --> -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> diff --git a/qpid/dotnet/TopicListener/TopicListener.csproj b/qpid/dotnet/TopicListener/TopicListener.csproj index a1e95cd668..46da42ea61 100644 --- a/qpid/dotnet/TopicListener/TopicListener.csproj +++ b/qpid/dotnet/TopicListener/TopicListener.csproj @@ -1,4 +1,4 @@ -<!-- +<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -19,7 +19,7 @@ --> -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> diff --git a/qpid/dotnet/TopicPublisher/TopicPublisher.csproj b/qpid/dotnet/TopicPublisher/TopicPublisher.csproj index 695d39ffe7..fbbf77fb8e 100644 --- a/qpid/dotnet/TopicPublisher/TopicPublisher.csproj +++ b/qpid/dotnet/TopicPublisher/TopicPublisher.csproj @@ -1,4 +1,4 @@ -<!-- +<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -19,7 +19,7 @@ --> -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> diff --git a/qpid/extras/qmf/.gitignore b/qpid/extras/qmf/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/qpid/extras/qmf/.gitignore @@ -0,0 +1 @@ +/build diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java index aa18b5a136..e552596058 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java @@ -31,7 +31,6 @@ import org.apache.qpid.server.configuration.ExchangeConfigType; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeReferrer; import org.apache.qpid.server.exchange.ExchangeType; -import org.apache.qpid.server.exchange.topic.TopicBinding; import org.apache.qpid.server.exchange.topic.TopicExchangeResult; import org.apache.qpid.server.exchange.topic.TopicMatcherResult; import org.apache.qpid.server.exchange.topic.TopicNormalizer; @@ -47,7 +46,6 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -55,6 +53,7 @@ import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicLong; public class ManagementExchange implements Exchange, QMFService.Listener @@ -69,8 +68,7 @@ public class ManagementExchange implements Exchange, QMFService.Listener private final Map<AMQShortString, TopicExchangeResult> _topicExchangeResults = new ConcurrentHashMap<AMQShortString, TopicExchangeResult>(); - private final Map<TopicBinding, FieldTable> _topicBindings = new HashMap<TopicBinding, FieldTable>(); - private final Set<Binding> _bindingSet = new HashSet<Binding>(); + private final Set<Binding> _bindingSet = new CopyOnWriteArraySet<Binding>(); private UUID _id; private static final String AGENT_BANK = "0"; @@ -254,21 +252,7 @@ public class ManagementExchange implements Exchange, QMFService.Listener public synchronized void addBinding(final Binding b) { - _bindingSet.add(b); - - for(BindingListener listener : _listeners) - { - listener.bindingAdded(this, b); - } - - if(_bindingSet.size() > _bindingCountHigh) - { - _bindingCountHigh = _bindingSet.size(); - } - - TopicBinding binding = new TopicBinding(new AMQShortString(b.getBindingKey()), b.getQueue(), null); - - if(!_topicBindings.containsKey(binding)) + if(_bindingSet.add(b)) { AMQShortString routingKey = TopicNormalizer.normalize(new AMQShortString(b.getBindingKey())); @@ -284,10 +268,20 @@ public class ManagementExchange implements Exchange, QMFService.Listener { result.addUnfilteredQueue(b.getQueue()); } - _topicBindings.put(binding, null); + result.addBinding(b); + } + + for(BindingListener listener : _listeners) + { + listener.bindingAdded(this, b); + } + if(_bindingSet.size() > _bindingCountHigh) + { + _bindingCountHigh = _bindingSet.size(); } + String bindingKey = b.getBindingKey(); if(bindingKey.startsWith("schema.") || bindingKey.startsWith("*.") || bindingKey.startsWith("#.")) @@ -355,6 +349,13 @@ public class ManagementExchange implements Exchange, QMFService.Listener HashSet<AMQQueue> queues = new HashSet<AMQQueue>(); for(TopicMatcherResult result : results) { + TopicExchangeResult res = (TopicExchangeResult)result; + + for(Binding b : res.getBindings()) + { + b.incrementMatches(); + } + queues.addAll(((TopicExchangeResult)result).getUnfilteredQueues()); } for(AMQQueue queue : queues) @@ -378,14 +379,11 @@ public class ManagementExchange implements Exchange, QMFService.Listener public synchronized void removeBinding(final Binding binding) { - _bindingSet.remove(binding); - - TopicBinding topicBinding = new TopicBinding(new AMQShortString(binding.getBindingKey()), binding.getQueue(), null); - - if(_topicBindings.containsKey(topicBinding)) + if(_bindingSet.remove(binding)) { - AMQShortString bindingKey = TopicNormalizer.normalize(topicBinding.getBindingKey()); + AMQShortString bindingKey = TopicNormalizer.normalize(new AMQShortString(binding.getBindingKey())); TopicExchangeResult result = _topicExchangeResults.get(bindingKey); + result.removeBinding(binding); result.removeUnfilteredQueue(binding.getQueue()); } 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 6d360b2084..381c376f56 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 @@ -1004,14 +1004,12 @@ public class QMFService implements ConfigStore.ConfigEventListener public Long getUnackedMessages() { - // TODO - return 0l; + return _obj.getUnackedMessageCount(); } public Long getUnackedMessagesHigh() { - // TODO - return 0l; + return _obj.getUnackedMessageCountHigh(); } public Long getUnackedMessagesLow() @@ -1404,8 +1402,7 @@ public class QMFService implements ConfigStore.ConfigEventListener public Long getDelivered() { - // TODO - return 0l; + return _obj.getDelivered(); } public UUID getId() 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 41ae52e9b8..be4e8f8ec1 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 @@ -110,7 +110,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr public String[] getExchangeTypes() throws IOException { ArrayList<String> exchangeTypes = new ArrayList<String>(); - for(ExchangeType<? extends Exchange> ex : _exchangeFactory.getRegisteredTypes()) + for(ExchangeType<? extends Exchange> ex : _exchangeFactory.getPublicCreatableTypes()) { exchangeTypes.add(ex.getName().toString()); } 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 3b17da5af7..fe5da20fa5 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 @@ -200,16 +200,22 @@ public class AMQChannel implements SessionConfig private void incrementOutstandingTxnsIfNecessary() { - //There can currently only be at most one outstanding transaction - //due to only having LocalTransaction support. Set value to 1 if 0. - _txnCount.compareAndSet(0,1); + if(isTransactional()) + { + //There can currently only be at most one outstanding transaction + //due to only having LocalTransaction support. Set value to 1 if 0. + _txnCount.compareAndSet(0,1); + } } private void decrementOutstandingTxnsIfNecessary() { - //There can currently only be at most one outstanding transaction - //due to only having LocalTransaction support. Set value to 0 if 1. - _txnCount.compareAndSet(1,0); + if(isTransactional()) + { + //There can currently only be at most one outstanding transaction + //due to only having LocalTransaction support. Set value to 0 if 1. + _txnCount.compareAndSet(1,0); + } } public Long getTxnStarts() @@ -313,7 +319,7 @@ public class AMQChannel implements SessionConfig } else { - _logger.warn("MESSAGE DISCARDED: No routes for message - " + createAMQMessage(_currentMessage,isTransactional())); + _logger.warn("MESSAGE DISCARDED: No routes for message - " + createAMQMessage(_currentMessage)); } } @@ -1031,7 +1037,7 @@ public class AMQChannel implements SessionConfig } - private AMQMessage createAMQMessage(IncomingMessage incomingMessage, boolean transactional) + private AMQMessage createAMQMessage(IncomingMessage incomingMessage) throws AMQException { @@ -1055,7 +1061,6 @@ public class AMQChannel implements SessionConfig private class MessageDeliveryAction implements ServerTransaction.Action { - private boolean _transactional; private IncomingMessage _incommingMessage; private ArrayList<? extends BaseQueue> _destinationQueues; @@ -1063,7 +1068,6 @@ public class AMQChannel implements SessionConfig ArrayList<? extends BaseQueue> destinationQueues, boolean transactional) { - _transactional = transactional; _incommingMessage = currentMessage; _destinationQueues = destinationQueues; } @@ -1074,7 +1078,7 @@ public class AMQChannel implements SessionConfig { final boolean immediate = _incommingMessage.isImmediate(); - final AMQMessage amqMessage = createAMQMessage(_incommingMessage, _transactional); + final AMQMessage amqMessage = createAMQMessage(_incommingMessage); MessageReference ref = amqMessage.newReference(); for(final BaseQueue queue : _destinationQueues) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java index 0b689c16a7..60c9a86b76 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java @@ -37,7 +37,7 @@ public class Binding private final UUID _id; private final AtomicLong _matches = new AtomicLong(); - Binding(UUID id, final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> arguments) + public Binding(UUID id, final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map<String, Object> arguments) { _id = id; _bindingKey = bindingKey; @@ -89,29 +89,30 @@ public class Binding @Override public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + { + return true; + } + + if (o == null || !(o instanceof Binding)) + { + return false; + } final Binding binding = (Binding) o; - if (!_bindingKey.equals(binding._bindingKey)) return false; - if (!_exchange.equals(binding._exchange)) return false; - if (!_queue.equals(binding._queue)) return false; - - return true; + return (_bindingKey == null ? binding.getBindingKey() == null : _bindingKey.equals(binding.getBindingKey())) + && (_exchange == null ? binding.getExchange() == null : _exchange.equals(binding.getExchange())) + && (_queue == null ? binding.getQueue() == null : _queue.equals(binding.getQueue())); } @Override public int hashCode() { - int result = _bindingKey.hashCode(); - result = 31 * result + _queue.hashCode(); - result = 31 * result + _exchange.hashCode(); + int result = _bindingKey == null ? 1 : _bindingKey.hashCode(); + result = 31 * result + (_queue == null ? 3 : _queue.hashCode()); + result = 31 * result + (_exchange == null ? 5 : _exchange.hashCode()); return result; } - - - - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfig.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfig.java index 95e2aa516b..4c9ec6619e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfig.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfig.java @@ -75,6 +75,10 @@ public interface QueueConfig extends ConfiguredObject<QueueConfigType, QueueConf long getPersistentMsgEnqueues(); long getPersistentMsgDequeues(); + + long getUnackedMessageCount(); + + long getUnackedMessageCountHigh(); void purge(long request); }
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/SubscriptionConfig.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/SubscriptionConfig.java index 985ecb2be9..b101d70553 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/SubscriptionConfig.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/SubscriptionConfig.java @@ -43,5 +43,5 @@ public interface SubscriptionConfig extends ConfiguredObject<SubscriptionConfigT boolean isExplicitAcknowledge(); - + Long getDelivered(); }
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 1c4c341c14..9be8bddd28 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -20,8 +20,12 @@ */ package org.apache.qpid.server.exchange; -import org.apache.log4j.Logger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQUnknownExchangeType; import org.apache.qpid.framing.AMQShortString; @@ -30,10 +34,6 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - public class DefaultExchangeFactory implements ExchangeFactory { private static final Logger _logger = Logger.getLogger(DefaultExchangeFactory.class); @@ -60,6 +60,21 @@ public class DefaultExchangeFactory implements ExchangeFactory { return _exchangeClassMap.values(); } + + public Collection<ExchangeType<? extends Exchange>> getPublicCreatableTypes() + { + Collection<ExchangeType<? extends Exchange>> publicTypes = + new ArrayList<ExchangeType<? extends Exchange>>(); + publicTypes.addAll(_exchangeClassMap.values()); + + //Remove the ManagementExchange type if present, as these + //are private and cannot be created by external means + publicTypes.remove(ManagementExchange.TYPE); + + return publicTypes; + } + + public Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) throws AMQException diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java index b91bf559f1..aa4cc1ec24 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java @@ -38,6 +38,8 @@ public interface ExchangeFactory void initialise(VirtualHostConfiguration hostConfig); Collection<ExchangeType<? extends Exchange>> getRegisteredTypes(); + + Collection<ExchangeType<? extends Exchange>> getPublicCreatableTypes(); Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) throws AMQException; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java index 35c4a8f9b2..f58a6513a9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java @@ -28,6 +28,7 @@ import java.util.Set; import org.apache.log4j.Logger; import org.apache.qpid.framing.AMQTypedValue; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.message.AMQMessageHeader; /** @@ -38,69 +39,35 @@ class HeadersBinding private static final Logger _logger = Logger.getLogger(HeadersBinding.class); private final FieldTable _mappings; + private final Binding _binding; private final Set<String> required = new HashSet<String>(); private final Map<String,Object> matches = new HashMap<String,Object>(); private boolean matchAny; - private final class MatchesOrProcessor implements FieldTable.FieldTableElementProcessor - { - private Boolean _result = Boolean.FALSE; - - public boolean processElement(String propertyName, AMQTypedValue value) - { - if((value != null) && (value.getValue() != null) && value.getValue().equals(matches.get(propertyName))) - { - _result = Boolean.TRUE; - return false; - } - return true; - } - - public Object getResult() - { - return _result; - } - } - - private final class RequiredOrProcessor implements FieldTable.FieldTableElementProcessor - { - Boolean _result = Boolean.FALSE; - - public boolean processElement(String propertyName, AMQTypedValue value) - { - if(required.contains(propertyName)) - { - _result = Boolean.TRUE; - return false; - } - return true; - } - - public Object getResult() - { - return _result; - } - } - - - /** - * Creates a binding for a set of mappings. Those mappings whose value is + * Creates a header binding for a set of mappings. Those mappings whose value is * null or the empty string are assumed only to be required headers, with * no constraint on the value. Those with a non-null value are assumed to * define a required match of value. - * @param mappings the defined mappings this binding should use + * + * @param binding the binding to create a header binding using */ - - HeadersBinding(FieldTable mappings) + public HeadersBinding(Binding binding) { - _mappings = mappings; - initMappings(); + _binding = binding; + if(_binding !=null) + { + _mappings = FieldTable.convertToFieldTable(_binding.getArguments()); + initMappings(); + } + else + { + _mappings = null; + } } - + private void initMappings() { - _mappings.processOverElements(new FieldTable.FieldTableElementProcessor() { @@ -133,6 +100,11 @@ class HeadersBinding { return _mappings; } + + public Binding getBinding() + { + return _binding; + } /** * Checks whether the supplied headers match the requirements of this binding @@ -250,4 +222,39 @@ class HeadersBinding { return key.startsWith("X-") || key.startsWith("x-"); } -} + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + + if (o == null) + { + return false; + } + + if (!(o instanceof HeadersBinding)) + { + return false; + } + + final HeadersBinding hb = (HeadersBinding) o; + + if(_binding == null) + { + if(hb.getBinding() != null) + { + return false; + } + } + else if (!_binding.equals(hb.getBinding())) + { + return false; + } + + return true; + } +}
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index ce0b14932f..e98a603d12 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -24,8 +24,6 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; @@ -36,10 +34,11 @@ import org.apache.qpid.server.binding.Binding; import javax.management.JMException; import java.util.ArrayList; -import java.util.List; +import java.util.LinkedHashSet; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; /** * An exchange that binds queues based on a set of required headers and header values @@ -72,7 +71,14 @@ public class HeadersExchange extends AbstractExchange { private static final Logger _logger = Logger.getLogger(HeadersExchange.class); - + + private final ConcurrentHashMap<String, CopyOnWriteArraySet<Binding>> _bindingsByKey = + new ConcurrentHashMap<String, CopyOnWriteArraySet<Binding>>(); + + private final CopyOnWriteArrayList<HeadersBinding> _bindingHeaderMatchers = + new CopyOnWriteArrayList<HeadersBinding>(); + + public static final ExchangeType<HeadersExchange> TYPE = new ExchangeType<HeadersExchange>() { @@ -102,34 +108,12 @@ public class HeadersExchange extends AbstractExchange } }; - - private final List<Registration> _bindings = new CopyOnWriteArrayList<Registration>(); - private Map<AMQShortString, Registration> _bindingByKey = new ConcurrentHashMap<AMQShortString, Registration>(); - - public HeadersExchange() { super(TYPE); } + - public void registerQueue(String routingKey, AMQQueue queue, Map<String,Object> args) - { - registerQueue(new AMQShortString(routingKey), queue, FieldTable.convertToFieldTable(args)); - } - - public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) - { - _logger.debug("Exchange " + getNameShortString() + ": Binding " + queue.getNameShortString() + " with " + args); - - Registration registration = new Registration(new HeadersBinding(args), queue, routingKey); - _bindings.add(registration); - - } - - public void deregisterQueue(String routingKey, AMQQueue queue, Map<String,Object> args) - { - _bindings.remove(new Registration(args == null ? null : new HeadersBinding(FieldTable.convertToFieldTable(args)), queue, new AMQShortString(routingKey))); - } public ArrayList<BaseQueue> doRoute(InboundMessage payload) { @@ -138,24 +122,27 @@ public class HeadersExchange extends AbstractExchange { _logger.debug("Exchange " + getNameShortString() + ": routing message with headers " + header); } - boolean routed = false; - ArrayList<BaseQueue> queues = new ArrayList<BaseQueue>(); - for (Registration e : _bindings) + + LinkedHashSet<BaseQueue> queues = new LinkedHashSet<BaseQueue>(); + + for (HeadersBinding hb : _bindingHeaderMatchers) { - - if (e.binding.matches(header)) + if (hb.matches(header)) { + Binding b = hb.getBinding(); + + b.incrementMatches(); + if (_logger.isDebugEnabled()) { _logger.debug("Exchange " + getNameShortString() + ": delivering message with headers " + - header + " to " + e.queue.getNameShortString()); + header + " to " + b.getQueue().getNameShortString()); } - queues.add(e.queue); - - routed = true; + queues.add(b.getQueue()); } } - return queues; + + return new ArrayList<BaseQueue>(queues); } public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) @@ -166,38 +153,49 @@ public class HeadersExchange extends AbstractExchange public boolean isBound(AMQShortString routingKey, AMQQueue queue) { - return isBound(queue); + String bindingKey = (routingKey == null) ? "" : routingKey.toString(); + CopyOnWriteArraySet<Binding> bindings = _bindingsByKey.get(bindingKey); + + if(bindings != null) + { + for(Binding binding : bindings) + { + if(binding.getQueue().equals(queue)) + { + return true; + } + } + } + + return false; } public boolean isBound(AMQShortString routingKey) { - return hasBindings(); + String bindingKey = (routingKey == null) ? "" : routingKey.toString(); + CopyOnWriteArraySet<Binding> bindings = _bindingsByKey.get(bindingKey); + return bindings != null && !bindings.isEmpty(); } public boolean isBound(AMQQueue queue) { - for (Registration r : _bindings) + for (CopyOnWriteArraySet<Binding> bindings : _bindingsByKey.values()) { - if (r.queue.equals(queue)) + for(Binding binding : bindings) { - return true; + if(binding.getQueue().equals(queue)) + { + return true; + } } } + return false; } public boolean hasBindings() { - return !_bindings.isEmpty(); - } - - - - protected FieldTable getHeaders(ContentHeaderBody contentHeaderFrame) - { - //what if the content type is not 'basic'? 'file' and 'stream' content classes also define headers, - //but these are not yet implemented. - return ((BasicContentHeaderProperties) contentHeaderFrame.properties).getHeaders(); + return !getBindings().isEmpty(); } protected AbstractExchangeMBean createMBean() throws JMException @@ -210,59 +208,51 @@ public class HeadersExchange extends AbstractExchange return _logger; } - - static class Registration + protected void onBind(final Binding binding) { - private final HeadersBinding binding; - private final AMQQueue queue; - private final AMQShortString routingKey; + String bindingKey = binding.getBindingKey(); + AMQQueue queue = binding.getQueue(); + AMQShortString routingKey = AMQShortString.valueOf(bindingKey); + Map<String,Object> args = binding.getArguments(); - Registration(HeadersBinding binding, AMQQueue queue, AMQShortString routingKey) - { - this.binding = binding; - this.queue = queue; - this.routingKey = routingKey; - } + assert queue != null; + assert routingKey != null; - public int hashCode() - { - int queueHash = queue.hashCode(); - int routingHash = routingKey == null ? 0 : routingKey.hashCode(); - return queueHash + routingHash; - } + CopyOnWriteArraySet<Binding> bindings = _bindingsByKey.get(bindingKey); - public boolean equals(Object o) + if(bindings == null) { - return o instanceof Registration - && ((Registration) o).queue.equals(queue) - && (routingKey == null ? ((Registration)o).routingKey == null - : routingKey.equals(((Registration)o).routingKey)); - } - - public HeadersBinding getBinding() - { - return binding; + bindings = new CopyOnWriteArraySet<Binding>(); + CopyOnWriteArraySet<Binding> newBindings; + if((newBindings = _bindingsByKey.putIfAbsent(bindingKey, bindings)) != null) + { + bindings = newBindings; + } } - - public AMQQueue getQueue() + + if(_logger.isDebugEnabled()) { - return queue; + _logger.debug("Exchange " + getNameShortString() + ": Binding " + queue.getNameShortString() + + " with binding key '" +bindingKey + "' and args: " + args); } - public AMQShortString getRoutingKey() - { - return routingKey; - } - } + _bindingHeaderMatchers.add(new HeadersBinding(binding)); + bindings.add(binding); - protected void onBind(final Binding binding) - { - registerQueue(binding.getBindingKey(), binding.getQueue(), binding.getArguments()); } protected void onUnbind(final Binding binding) { - deregisterQueue(binding.getBindingKey(), binding.getQueue(), binding.getArguments()); + assert binding != null; + + CopyOnWriteArraySet<Binding> bindings = _bindingsByKey.get(binding.getBindingKey()); + if(bindings != null) + { + bindings.remove(binding); + } + + _logger.debug("==============="); + _logger.debug("Removing Binding: " + _bindingHeaderMatchers.remove(new HeadersBinding(binding))); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java index 14f15dd92c..1245efdafa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -31,6 +31,7 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.exchange.topic.*; import org.apache.qpid.server.filter.JMSSelectorFilter; import org.apache.qpid.server.message.InboundMessage; @@ -83,7 +84,7 @@ public class TopicExchange extends AbstractExchange private final Map<AMQShortString, TopicExchangeResult> _topicExchangeResults = new ConcurrentHashMap<AMQShortString, TopicExchangeResult>(); - private final Map<TopicBinding, FieldTable> _bindings = new HashMap<TopicBinding, FieldTable>(); + private final Map<Binding, FieldTable> _bindings = new HashMap<Binding, FieldTable>(); private final Map<String, WeakReference<JMSSelectorFilter>> _selectorCache = new WeakHashMap<String, WeakReference<JMSSelectorFilter>>(); @@ -92,20 +93,12 @@ public class TopicExchange extends AbstractExchange super(TYPE); } - public synchronized void registerQueue(String rKey, AMQQueue queue, Map<String,Object> args) - { - try - { - registerQueue(new AMQShortString(rKey), queue, FieldTable.convertToFieldTable(args)); - } - catch (AMQInvalidArgumentException e) - { - throw new RuntimeException(e); - } - } - - public synchronized void registerQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQInvalidArgumentException + protected synchronized void registerQueue(final Binding binding) throws AMQInvalidArgumentException { + AMQShortString rKey = new AMQShortString(binding.getBindingKey()) ; + AMQQueue queue = binding.getQueue(); + FieldTable args = FieldTable.convertToFieldTable(binding.getArguments()); + assert queue != null; assert rKey != null; @@ -114,8 +107,6 @@ public class TopicExchange extends AbstractExchange AMQShortString routingKey = TopicNormalizer.normalize(rKey); - TopicBinding binding = new TopicBinding(rKey, queue, args); - if(_bindings.containsKey(binding)) { FieldTable oldArgs = _bindings.get(binding); @@ -146,6 +137,8 @@ public class TopicExchange extends AbstractExchange return; } } + + result.addBinding(binding); } else @@ -177,6 +170,8 @@ public class TopicExchange extends AbstractExchange result.addUnfilteredQueue(queue); } } + + result.addBinding(binding); _bindings.put(binding, args); } @@ -226,7 +221,8 @@ public class TopicExchange extends AbstractExchange public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) { - TopicBinding binding = new TopicBinding(routingKey, queue, arguments); + Binding binding = new Binding(null, routingKey.toString(), queue, this, FieldTable.convertToMap(arguments)); + if (arguments == null) { return _bindings.containsKey(binding); @@ -253,7 +249,7 @@ public class TopicExchange extends AbstractExchange public boolean isBound(AMQShortString routingKey) { - for(TopicBinding b : _bindings.keySet()) + for(Binding b : _bindings.keySet()) { if(b.getBindingKey().equals(routingKey)) { @@ -266,7 +262,7 @@ public class TopicExchange extends AbstractExchange public boolean isBound(AMQQueue queue) { - for(TopicBinding b : _bindings.keySet()) + for(Binding b : _bindings.keySet()) { if(b.getQueue().equals(queue)) { @@ -282,19 +278,16 @@ public class TopicExchange extends AbstractExchange return !_bindings.isEmpty(); } - - public void deregisterQueue(String rKey, AMQQueue queue, Map<String, Object> args) - { - removeBinding(new TopicBinding(new AMQShortString(rKey), queue, FieldTable.convertToFieldTable(args))); - } - - private boolean removeBinding(final TopicBinding binding) + private boolean deregisterQueue(final Binding binding) { if(_bindings.containsKey(binding)) { FieldTable bindingArgs = _bindings.remove(binding); - AMQShortString bindingKey = TopicNormalizer.normalize(binding.getBindingKey()); + AMQShortString bindingKey = TopicNormalizer.normalize(new AMQShortString(binding.getBindingKey())); TopicExchangeResult result = _topicExchangeResults.get(bindingKey); + + result.removeBinding(binding); + if(argumentsContainSelector(bindingArgs)) { try @@ -341,8 +334,14 @@ public class TopicExchange extends AbstractExchange Collection<AMQQueue> queues = results.size() == 1 ? null : new HashSet<AMQQueue>(); for(TopicMatcherResult result : results) { + TopicExchangeResult res = (TopicExchangeResult)result; - queues = ((TopicExchangeResult)result).processMessage(message, queues); + for(Binding b : res.getBindings()) + { + b.incrementMatches(); + } + + queues = res.processMessage(message, queues); } return queues; } @@ -350,14 +349,21 @@ public class TopicExchange extends AbstractExchange } - protected void onBind(final org.apache.qpid.server.binding.Binding binding) + protected void onBind(final Binding binding) { - registerQueue(binding.getBindingKey(),binding.getQueue(),binding.getArguments()); + try + { + registerQueue(binding); + } + catch (AMQInvalidArgumentException e) + { + throw new RuntimeException(e); + } } - protected void onUnbind(final org.apache.qpid.server.binding.Binding binding) + protected void onUnbind(final Binding binding) { - deregisterQueue(binding.getBindingKey(),binding.getQueue(),binding.getArguments()); + deregisterQueue(binding); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicBinding.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicBinding.java deleted file mode 100644 index c6383a886e..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicBinding.java +++ /dev/null @@ -1,70 +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. - * - */ -package org.apache.qpid.server.exchange.topic; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.exchange.TopicExchange; - -public class TopicBinding -{ - private final AMQShortString _bindingKey; - private final AMQQueue _queue; - private final FieldTable _args; - - public TopicBinding(AMQShortString bindingKey, AMQQueue queue, FieldTable args) - { - _bindingKey = bindingKey; - _queue = queue; - _args = args; - } - - public AMQShortString getBindingKey() - { - return _bindingKey; - } - - public AMQQueue getQueue() - { - return _queue; - } - - public int hashCode() - { - return (_bindingKey == null ? 1 : _bindingKey.hashCode())*31 +_queue.hashCode(); - } - - public boolean equals(Object o) - { - if(this == o) - { - return true; - } - if(o instanceof TopicBinding) - { - TopicBinding other = (TopicBinding) o; - return (_queue == other._queue) - && ((_bindingKey == null) ? other._bindingKey == null : _bindingKey.equals(other._bindingKey)); - } - return false; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicExchangeResult.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicExchangeResult.java index d9a779802f..41dc0d749a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicExchangeResult.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicExchangeResult.java @@ -21,14 +21,22 @@ package org.apache.qpid.server.exchange.topic; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.filter.MessageFilter; import org.apache.qpid.server.message.InboundMessage; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; public final class TopicExchangeResult implements TopicMatcherResult { + private final List<Binding> _bindings = new CopyOnWriteArrayList<Binding>(); private final Map<AMQQueue, Integer> _unfilteredQueues = new ConcurrentHashMap<AMQQueue, Integer>(); private final ConcurrentHashMap<AMQQueue, Map<MessageFilter,Integer>> _filteredQueues = new ConcurrentHashMap<AMQQueue, Map<MessageFilter, Integer>>(); @@ -64,6 +72,20 @@ public final class TopicExchangeResult implements TopicMatcherResult return _unfilteredQueues.keySet(); } + public void addBinding(Binding binding) + { + _bindings.add(binding); + } + + public void removeBinding(Binding binding) + { + _bindings.remove(binding); + } + + public List<Binding> getBindings() + { + return new ArrayList<Binding>(_bindings); + } public void addFilteredQueue(AMQQueue queue, MessageFilter filter) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index d3867d8140..45c84d7603 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -111,6 +111,7 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer void dequeue(QueueEntry entry, Subscription sub); + void decrementUnackedMsgCount(); boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index bf4015eb7a..1ba4f4d89b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -226,22 +226,29 @@ public class QueueEntryImpl implements QueueEntry public void release() { - _stateUpdater.set(this,AVAILABLE_STATE); - if(!getQueue().isDeleted()) + EntryState state = _state; + + if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, AVAILABLE_STATE)) { - getQueue().requeue(this); - if(_stateChangeListeners != null) + if(state instanceof SubscriptionAcquiredState) { - notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE); + getQueue().decrementUnackedMsgCount(); } + + if(!getQueue().isDeleted()) + { + getQueue().requeue(this); + if(_stateChangeListeners != null) + { + notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE); + } + } + else if(acquire()) + { + routeToAlternate(); + } } - else if(acquire()) - { - routeToAlternate(); - } - - } public boolean releaseButRetain() @@ -369,6 +376,7 @@ public class QueueEntryImpl implements QueueEntry Subscription s = null; if (state instanceof SubscriptionAcquiredState) { + getQueue().decrementUnackedMsgCount(); s = ((SubscriptionAcquiredState) state).getSubscription(); s.onDequeue(this); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index b5d1290e98..cf2f637697 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -127,6 +127,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private final AtomicLong _byteTxnEnqueues = new AtomicLong(0); private final AtomicLong _msgTxnDequeues = new AtomicLong(0); private final AtomicLong _byteTxnDequeues = new AtomicLong(0); + private final AtomicLong _unackedMsgCount = new AtomicLong(0); + private final AtomicLong _unackedMsgCountHigh = new AtomicLong(0); private final AtomicInteger _bindingCountHigh = new AtomicInteger(); @@ -693,6 +695,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener throws AMQException { _deliveredMessages.incrementAndGet(); + incrementUnackedMsgCount(); + sub.send(entry); setLastSeenEntry(sub,entry); @@ -2138,4 +2142,33 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { return String.valueOf(getNameShortString()); } + + public long getUnackedMessageCountHigh() + { + return _unackedMsgCountHigh.get(); + } + + public long getUnackedMessageCount() + { + return _unackedMsgCount.get(); + } + + public void decrementUnackedMsgCount() + { + _unackedMsgCount.decrementAndGet(); + } + + private void incrementUnackedMsgCount() + { + long unackedMsgCount = _unackedMsgCount.incrementAndGet(); + + long unackedMsgCountHigh; + while(unackedMsgCount > (unackedMsgCountHigh = _unackedMsgCountHigh.get())) + { + if(_unackedMsgCountHigh.compareAndSet(unackedMsgCountHigh, unackedMsgCount)) + { + break; + } + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java index 1309e05978..46c1a6af9a 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java @@ -30,7 +30,7 @@ import org.apache.log4j.Logger; class SubFlushRunner implements ReadWriteRunnable { - private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.class); + private static final Logger _logger = Logger.getLogger(SubFlushRunner.class); private final Subscription _sub; @@ -46,29 +46,36 @@ class SubFlushRunner implements ReadWriteRunnable public void run() { - - Thread.currentThread().setName(_name); - - boolean complete = false; + String originalName = Thread.currentThread().getName(); try { - CurrentActor.set(_sub.getLogActor()); - complete = getQueue().flushSubscription(_sub, ITERATIONS); + Thread.currentThread().setName(_name); + + boolean complete = false; + try + { + CurrentActor.set(_sub.getLogActor()); + complete = getQueue().flushSubscription(_sub, ITERATIONS); + + } + catch (AMQException e) + { + _logger.error(e); + } + finally + { + CurrentActor.remove(); + } + if (!complete && !_sub.isSuspended()) + { + getQueue().execute(this); + } } - catch (AMQException e) - { - _logger.error(e); - } finally { - CurrentActor.remove(); + Thread.currentThread().setName(originalName); } - if (!complete && !_sub.isSuspended()) - { - getQueue().execute(this); - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index c548f3ccad..eed57fbf39 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -94,6 +94,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage private LogSubject _logSubject; private LogActor _logActor; private UUID _id; + private final AtomicLong _deliveredCount = new AtomicLong(0); private long _createTime = System.currentTimeMillis(); @@ -340,7 +341,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { return getQueue().getConfigStore(); } - + + public Long getDelivered() + { + return _deliveredCount.get(); + } public synchronized void setQueue(AMQQueue queue, boolean exclusive) { @@ -648,6 +653,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage throws AMQException { _deliveryMethod.deliverToClient(this,entry,deliveryTag); + _deliveredCount.incrementAndGet(); } 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 4cc7e6fce2..5b3f5250c5 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 @@ -101,6 +101,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private String _traceExclude; private String _trace; private long _createTime = System.currentTimeMillis(); + private final AtomicLong _deliveredCount = new AtomicLong(0); public Subscription_0_10(ServerSession session, String destination, MessageAcceptMode acceptMode, @@ -257,6 +258,11 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { return getQueue().getConfigStore(); } + + public Long getDelivered() + { + return _deliveredCount.get(); + } public void creditStateChanged(boolean hasCredit) { @@ -531,7 +537,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr if(!excludeDueToFederation) { - if(_acceptMode == MessageAcceptMode.NONE) + if(_acceptMode == MessageAcceptMode.NONE && _acquireMode != MessageAcquireMode.PRE_ACQUIRED) { xfr.setCompletionListener(new MessageAcceptCompletionListener(this, _session, entry, _flowMode == MessageFlowMode.WINDOW)); } @@ -558,7 +564,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } _session.sendMessage(xfr, _postIdSettingAction); - + _deliveredCount.incrementAndGet(); if(_acceptMode == MessageAcceptMode.NONE && _acquireMode == MessageAcquireMode.PRE_ACQUIRED) { forceDequeue(entry, false); 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 63d540be6b..b5d5d7bba9 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 @@ -401,6 +401,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo public void selectTx() { _transaction = new LocalTransaction(this.getMessageStore()); + _txnStarts.incrementAndGet(); } public void commit() @@ -424,16 +425,22 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo private void incrementOutstandingTxnsIfNecessary() { - //There can currently only be at most one outstanding transaction - //due to only having LocalTransaction support. Set value to 1 if 0. - _txnCount.compareAndSet(0,1); + if(isTransactional()) + { + //There can currently only be at most one outstanding transaction + //due to only having LocalTransaction support. Set value to 1 if 0. + _txnCount.compareAndSet(0,1); + } } private void decrementOutstandingTxnsIfNecessary() { - //There can currently only be at most one outstanding transaction - //due to only having LocalTransaction support. Set value to 0 if 1. - _txnCount.compareAndSet(1,0); + if(isTransactional()) + { + //There can currently only be at most one outstanding transaction + //due to only having LocalTransaction support. Set value to 0 if 1. + _txnCount.compareAndSet(1,0); + } } public Long getTxnStarts() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java index 06d5d80ac1..2d8b157297 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java @@ -31,6 +31,7 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.AMQMessageHeader; @@ -53,8 +54,10 @@ import org.apache.qpid.server.subscription.Subscription; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @@ -92,31 +95,31 @@ public class AbstractHeadersExchangeTestBase extends TestCase protected TestQueue bindDefault(String... bindings) throws AMQException { - return bind("Queue" + (++count), bindings); - } + String queueName = "Queue" + (++count); - protected TestQueue bind(String queueName, String... bindings) throws AMQException - { - return bind(queueName, getHeaders(bindings)); + return bind(queueName, queueName, getHeadersMap(bindings)); } - - protected TestQueue bind(String queue, FieldTable bindings) throws AMQException + + protected void unbind(TestQueue queue, String... bindings) throws AMQException { - return bind(new TestQueue(new AMQShortString(queue)), bindings); + String queueName = queue.getName(); + //TODO - check this + exchange.onUnbind(new Binding(null,queueName, queue, exchange, getHeadersMap(bindings))); } - - protected TestQueue bind(TestQueue queue, String... bindings) throws AMQException + + protected int getCount() { - return bind(queue, getHeaders(bindings)); + return count; } - protected TestQueue bind(TestQueue queue, FieldTable bindings) throws AMQException + private TestQueue bind(String key, String queueName, Map<String,Object> args) throws AMQException { + TestQueue queue = new TestQueue(new AMQShortString(queueName)); queues.add(queue); - exchange.registerQueue(null, queue, bindings); + exchange.onBind(new Binding(null,key, queue, exchange, args)); return queue; } - + protected int route(Message m) throws AMQException { @@ -171,6 +174,23 @@ public class AbstractHeadersExchangeTestBase extends TestCase } } + + static Map<String,Object> getHeadersMap(String... entries) + { + if(entries == null) + { + return null; + } + + Map<String,Object> headers = new HashMap<String,Object>(); + + for (String s : entries) + { + String[] parts = s.split("=", 2); + headers.put(parts[0], parts.length > 1 ? parts[1] : ""); + } + return headers; + } static FieldTable getHeaders(String... entries) { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java index 1e56a32383..a7c226cbd8 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java @@ -26,7 +26,9 @@ import java.util.Set; import junit.framework.TestCase; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.message.AMQMessageHeader; +import org.apache.qpid.server.queue.MockAMQQueue; /** */ @@ -119,166 +121,193 @@ public class HeadersBindingTest extends TestCase } } - private FieldTable bindHeaders = new FieldTable(); + private Map<String,Object> bindHeaders = new HashMap<String,Object>(); private MockHeader matchHeaders = new MockHeader(); + private int _count = 0; + private MockAMQQueue _queue; + + protected void setUp() + { + _count++; + _queue = new MockAMQQueue(getQueueName()); + } + + protected String getQueueName() + { + return "Queue" + _count; + } public void testDefault_1() { - bindHeaders.setString("A", "Value of A"); + bindHeaders.put("A", "Value of A"); matchHeaders.setString("A", "Value of A"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testDefault_2() { - bindHeaders.setString("A", "Value of A"); + bindHeaders.put("A", "Value of A"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Value of B"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testDefault_3() { - bindHeaders.setString("A", "Value of A"); + bindHeaders.put("A", "Value of A"); matchHeaders.setString("A", "Altered value of A"); - assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertFalse(new HeadersBinding(b).matches(matchHeaders)); } public void testAll_1() { - bindHeaders.setString("X-match", "all"); - bindHeaders.setString("A", "Value of A"); + bindHeaders.put("X-match", "all"); + bindHeaders.put("A", "Value of A"); matchHeaders.setString("A", "Value of A"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAll_2() { - bindHeaders.setString("X-match", "all"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "all"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); - assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertFalse(new HeadersBinding(b).matches(matchHeaders)); } public void testAll_3() { - bindHeaders.setString("X-match", "all"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "all"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Value of B"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAll_4() { - bindHeaders.setString("X-match", "all"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "all"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Value of B"); matchHeaders.setString("C", "Value of C"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAll_5() { - bindHeaders.setString("X-match", "all"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "all"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Altered value of B"); matchHeaders.setString("C", "Value of C"); - assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertFalse(new HeadersBinding(b).matches(matchHeaders)); } public void testAny_1() { - bindHeaders.setString("X-match", "any"); - bindHeaders.setString("A", "Value of A"); + bindHeaders.put("X-match", "any"); + bindHeaders.put("A", "Value of A"); matchHeaders.setString("A", "Value of A"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAny_2() { - bindHeaders.setString("X-match", "any"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "any"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAny_3() { - bindHeaders.setString("X-match", "any"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "any"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Value of B"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAny_4() { - bindHeaders.setString("X-match", "any"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "any"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Value of B"); matchHeaders.setString("C", "Value of C"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAny_5() { - bindHeaders.setString("X-match", "any"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "any"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Value of A"); matchHeaders.setString("B", "Altered value of B"); matchHeaders.setString("C", "Value of C"); - assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertTrue(new HeadersBinding(b).matches(matchHeaders)); } public void testAny_6() { - bindHeaders.setString("X-match", "any"); - bindHeaders.setString("A", "Value of A"); - bindHeaders.setString("B", "Value of B"); + bindHeaders.put("X-match", "any"); + bindHeaders.put("A", "Value of A"); + bindHeaders.put("B", "Value of B"); matchHeaders.setString("A", "Altered value of A"); matchHeaders.setString("B", "Altered value of B"); matchHeaders.setString("C", "Value of C"); - assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + Binding b = new Binding(null, getQueueName(), _queue, null, bindHeaders); + assertFalse(new HeadersBinding(b).matches(matchHeaders)); } public static junit.framework.Test suite() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java index 580bc78b8d..f982c3976f 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java @@ -106,6 +106,22 @@ public class HeadersExchangeTest extends AbstractHeadersExchangeTestBase pb2.setMandatory(true); routeAndTest(m1,true); } + + public void testOnUnbind() throws AMQException + { + TestQueue q1 = bindDefault("F0000"); + TestQueue q2 = bindDefault("F0000=Aardvark"); + TestQueue q3 = bindDefault("F0001"); + + routeAndTest(new Message(_protocolSession, "Message1", "F0000"), q1); + routeAndTest(new Message(_protocolSession, "Message2", "F0000=Aardvark"), q1, q2); + routeAndTest(new Message(_protocolSession, "Message3", "F0001"), q3); + + unbind(q1,"F0000"); + routeAndTest(new Message(_protocolSession, "Message4", "F0000")); + routeAndTest(new Message(_protocolSession, "Message5", "F0000=Aardvark"), q2); + } + public static junit.framework.Test suite() { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java index daa0377e0a..4fa47d039e 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java @@ -28,6 +28,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.MemoryMessageStore; import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.AMQException; @@ -64,7 +65,7 @@ public class TopicExchangeTest extends TestCase public void testNoRoute() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + _exchange.registerQueue(new Binding(null,"a.*.#.b", queue,_exchange, null)); IncomingMessage message = createMessage("a.b"); @@ -76,7 +77,7 @@ public class TopicExchangeTest extends TestCase public void testDirectMatch() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + _exchange.registerQueue(new Binding(null,"a.b", queue,_exchange, null)); IncomingMessage message = createMessage("a.b"); @@ -103,7 +104,7 @@ public class TopicExchangeTest extends TestCase public void testStarMatch() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.*"), queue, null); + _exchange.registerQueue(new Binding(null,"a.*", queue,_exchange, null)); IncomingMessage message = createMessage("a.b"); @@ -142,7 +143,7 @@ public class TopicExchangeTest extends TestCase public void testHashMatch() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.#"), queue, null); + _exchange.registerQueue(new Binding(null,"a.#", queue,_exchange, null)); IncomingMessage message = createMessage("a.b.c"); @@ -205,7 +206,7 @@ public class TopicExchangeTest extends TestCase public void testMidHash() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + _exchange.registerQueue(new Binding(null,"a.*.#.b", queue,_exchange, null)); IncomingMessage message = createMessage("a.c.d.b"); @@ -235,7 +236,7 @@ public class TopicExchangeTest extends TestCase public void testMatchafterHash() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null); + _exchange.registerQueue(new Binding(null,"a.*.#.b.c", queue,_exchange, null)); IncomingMessage message = createMessage("a.c.b.b"); @@ -281,7 +282,7 @@ public class TopicExchangeTest extends TestCase public void testHashAfterHash() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null); + _exchange.registerQueue(new Binding(null,"a.*.#.b.c.#.d", queue,_exchange, null)); IncomingMessage message = createMessage("a.c.b.b.c"); @@ -308,7 +309,7 @@ public class TopicExchangeTest extends TestCase public void testHashHash() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null); + _exchange.registerQueue(new Binding(null,"a.#.*.#.d", queue,_exchange, null)); IncomingMessage message = createMessage("a.c.b.b.c"); @@ -334,7 +335,7 @@ public class TopicExchangeTest extends TestCase public void testSubMatchFails() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null); + _exchange.registerQueue(new Binding(null,"a.b.c.d", queue,_exchange, null)); IncomingMessage message = createMessage("a.b.c"); @@ -364,7 +365,7 @@ public class TopicExchangeTest extends TestCase public void testMoreRouting() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + _exchange.registerQueue(new Binding(null,"a.b", queue,_exchange, null)); IncomingMessage message = createMessage("a.b.c"); @@ -379,7 +380,7 @@ public class TopicExchangeTest extends TestCase public void testMoreQueue() throws AMQException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); - _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + _exchange.registerQueue(new Binding(null,"a.b", queue,_exchange, null)); IncomingMessage message = createMessage("a"); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index 7063beefca..dbd51af68c 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -572,4 +572,19 @@ public class MockAMQQueue implements AMQQueue { return 0; } + + public void decrementUnackedMsgCount() + { + + } + + public long getUnackedMessageCount() + { + return 0; + } + + public long getUnackedMessageCountHigh() + { + return 0; + } } 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 5ad297580e..4717a9495b 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 @@ -157,45 +157,9 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec + _conn.getPassword()); } - String saslMechs = brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_MECHS) != null ? - brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_MECHS): - System.getProperty("qpid.sasl_mechs", "PLAIN"); - - // Sun SASL Kerberos client uses the - // protocol + servername as the service key. - String protocol = brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME) != null ? - brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME): - System.getProperty("qpid.sasl_protocol", "AMQP"); - - String saslServerName = brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME) != null ? - brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME): - System.getProperty("qpid.sasl_server_name", "localhost"); - - boolean useSSL = brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SSL); - - boolean useSASLEncryption = brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SASL_ENCRYPTION)? - brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SASL_ENCRYPTION): - Boolean.getBoolean("qpid.sasl_encryption"); - - boolean useTcpNodelay = brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY)? - brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY): - Boolean.getBoolean("amqj.tcp_nodelay"); - - ConnectionSettings conSettings = new ConnectionSettings(); - conSettings.setHost(brokerDetail.getHost()); - conSettings.setPort(brokerDetail.getPort()); - conSettings.setVhost(_conn.getVirtualHost()); - conSettings.setUsername(_conn.getUsername()); - conSettings.setPassword(_conn.getPassword()); - conSettings.setUseSASLEncryption(useSASLEncryption); - conSettings.setUseSSL(useSSL); - conSettings.setSaslMechs(saslMechs); - conSettings.setTcpNodelay(useTcpNodelay); - conSettings.setSaslProtocol(protocol); - conSettings.setSaslServerName(saslServerName); - conSettings.setHeartbeatInterval(getHeartbeatInterval(brokerDetail)); - + retriveConnectionSettings(conSettings,brokerDetail); + _qpidConnection.connect(conSettings); _conn._connected = true; @@ -328,6 +292,87 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec return ProtocolVersion.v0_10; } + private void retriveConnectionSettings(ConnectionSettings conSettings, BrokerDetails brokerDetail) + { + + conSettings.setHost(brokerDetail.getHost()); + conSettings.setPort(brokerDetail.getPort()); + conSettings.setVhost(_conn.getVirtualHost()); + conSettings.setUsername(_conn.getUsername()); + conSettings.setPassword(_conn.getPassword()); + + // ------------ sasl options --------------- + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_MECHS) != null) + { + conSettings.setSaslMechs( + brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_MECHS)); + } + + // Sun SASL Kerberos client uses the + // protocol + servername as the service key. + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME) != null) + { + conSettings.setSaslProtocol( + brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_PROTOCOL_NAME)); + } + + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME) != null) + { + conSettings.setSaslServerName( + brokerDetail.getProperty(BrokerDetails.OPTIONS_SASL_SERVER_NAME)); + } + + conSettings.setUseSASLEncryption( + brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SASL_ENCRYPTION)); + + // ------------- ssl options --------------------- + conSettings.setUseSSL(brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SSL)); + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE) != null) + { + conSettings.setTrustStorePath( + brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE)); + } + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE_PASSWORD) != null) + { + conSettings.setTrustStorePassword( + brokerDetail.getProperty(BrokerDetails.OPTIONS_TRUST_STORE_PASSWORD)); + } + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE) != null) + { + conSettings.setKeyStorePath( + brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE)); + } + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE_PASSWORD) != null) + { + conSettings.setKeyStorePassword( + brokerDetail.getProperty(BrokerDetails.OPTIONS_KEY_STORE_PASSWORD)); + } + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS) != null) + { + conSettings.setCertAlias( + brokerDetail.getProperty(BrokerDetails.OPTIONS_SSL_CERT_ALIAS)); + } + // ---------------------------- + + conSettings.setVerifyHostname(brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_SSL_VERIFY_HOSTNAME)); + + + if (brokerDetail.getProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY) != null) + { + conSettings.setTcpNodelay( + brokerDetail.getBooleanProperty(BrokerDetails.OPTIONS_TCP_NO_DELAY)); + } + + conSettings.setHeartbeatInterval(getHeartbeatInterval(brokerDetail)); + } + // The idle_timeout prop is in milisecs while // the new heartbeat prop is in secs private int getHeartbeatInterval(BrokerDetails brokerDetail) @@ -353,4 +398,9 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec } return 0; } + + protected org.apache.qpid.transport.Connection getQpidConnection() + { + return _qpidConnection; + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTestConnection_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTestConnection_0_10.java new file mode 100644 index 0000000000..540b6042bf --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTestConnection_0_10.java @@ -0,0 +1,16 @@ +package org.apache.qpid.client; + +import org.apache.qpid.transport.Connection; + +public class AMQTestConnection_0_10 extends AMQConnection +{ + public AMQTestConnection_0_10(String url) throws Exception + { + super(url); + } + + public Connection getConnection() + { + return((AMQConnectionDelegate_0_10)_delegate).getQpidConnection(); + } +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java b/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java index 35adda9348..4f4fc3ddd3 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java @@ -48,8 +48,13 @@ public class XAResourceImpl implements XAResource */ private Xid _xid; + /** + * The time for this resource + */ + private int _timeout; + //--- constructor - + /** * Create an XAResource associated with a XASession * @@ -90,6 +95,10 @@ public class XAResourceImpl implements XAResource _xaSession.createSession(); convertExecutionErrorToXAErr(e.getException().getErrorCode()); } + finally + { + _xid = null; + } checkStatus(result.getStatus()); } @@ -171,6 +180,10 @@ public class XAResourceImpl implements XAResource _xaSession.createSession(); convertExecutionErrorToXAErr(e.getException().getErrorCode()); } + finally + { + _xid = null; + } } @@ -178,30 +191,13 @@ public class XAResourceImpl implements XAResource * Obtains the current transaction timeout value set for this XAResource instance. * If XAResource.setTransactionTimeout was not used prior to invoking this method, * the return value is the default timeout i.e. 0; - * otherwise, the value used in the previous setTransactionTimeout call is returned. * * @return The transaction timeout value in seconds. * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL. */ public int getTransactionTimeout() throws XAException { - int result = 0; - if (_xid != null) - { - Future<GetTimeoutResult> future = - _xaSession.getQpidSession().dtxGetTimeout(convertXid(_xid)); - try - { - result = (int) future.get().getTimeout(); - } - catch (SessionException e) - { - // we need to restore the qpid session that has been closed - _xaSession.createSession(); - convertExecutionErrorToXAErr(e.getException().getErrorCode()); - } - } - return result; + return _timeout; } /** @@ -325,6 +321,10 @@ public class XAResourceImpl implements XAResource _xaSession.createSession(); convertExecutionErrorToXAErr( e.getException().getErrorCode()); } + finally + { + _xid = null; + } checkStatus(result.getStatus()); } @@ -340,25 +340,29 @@ public class XAResourceImpl implements XAResource */ public boolean setTransactionTimeout(int timeout) throws XAException { - boolean result = false; - if (_xid != null) + _timeout = timeout; + if (timeout != _timeout && _xid != null) + { + setDtxTimeout(_timeout); + } + return true; + } + + private void setDtxTimeout(int timeout) throws XAException + { + try { - try - { - _xaSession.getQpidSession() - .dtxSetTimeout(XidImpl.convert(_xid), timeout); - } - catch (QpidException e) + _xaSession.getQpidSession() + .dtxSetTimeout(XidImpl.convert(_xid), timeout); + } + catch (QpidException e) + { + if (_logger.isDebugEnabled()) { - if (_logger.isDebugEnabled()) - { - _logger.debug("Cannot convert Xid into String format ", e); - } - throw new XAException(XAException.XAER_PROTO); + _logger.debug("Cannot convert Xid into String format ", e); } - result = true; + throw new XAException(XAException.XAER_PROTO); } - return result; } /** @@ -413,6 +417,10 @@ public class XAResourceImpl implements XAResource } checkStatus(result.getStatus()); _xid = xid; + if (_timeout > 0) + { + setDtxTimeout(_timeout); + } } //------------------------------------------------------------------------ @@ -477,7 +485,15 @@ public class XAResourceImpl implements XAResource throw new XAException(XAException.XAER_DUPID); case NOT_FOUND: // The XID is not valid. - throw new XAException(XAException.XAER_NOTA); + try + { + throw new XAException(XAException.XAER_NOTA); + } + catch (XAException e) + { + e.printStackTrace(); + throw e; + } case ILLEGAL_STATE: // Routine was invoked in an inproper context. throw new XAException(XAException.XAER_PROTO); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java index c09472fcad..6d81f728c9 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java @@ -42,6 +42,14 @@ public interface BrokerDetails public static final String OPTIONS_TCP_NO_DELAY = "tcp_nodelay"; public static final String OPTIONS_SASL_PROTOCOL_NAME = "sasl_protocol"; public static final String OPTIONS_SASL_SERVER_NAME = "sasl_server"; + + public static final String OPTIONS_TRUST_STORE = "trust_store"; + public static final String OPTIONS_TRUST_STORE_PASSWORD = "trust_store_password"; + public static final String OPTIONS_KEY_STORE = "key_store"; + public static final String OPTIONS_KEY_STORE_PASSWORD = "key_store_password"; + public static final String OPTIONS_SSL_VERIFY_HOSTNAME = "ssl_verify_hostname"; + public static final String OPTIONS_SSL_CERT_ALIAS = "ssl_cert_alias"; + public static final int DEFAULT_PORT = 5672; public static final String SOCKET = "socket"; diff --git a/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java index e142d21e06..702746b3da 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java @@ -27,10 +27,13 @@ import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; +import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; + /** * Factory used to create SSLContexts. SSL needs to be configured * before this will work. @@ -68,7 +71,7 @@ public class SSLContextFactory { */ private String _trustStoreCertType; - + private KeyManager customKeyManager; public SSLContextFactory(String trustStorePath, String trustStorePassword, String trustStoreCertType) @@ -90,7 +93,7 @@ public class SSLContextFactory { _trustStorePath = trustStorePath; _trustStorePassword = trustStorePassword; - if (_trustStorePassword.equals("none")) + if (_trustStorePassword != null && _trustStorePassword.equals("none")) { _trustStorePassword = null; } @@ -99,7 +102,7 @@ public class SSLContextFactory { _keyStorePath = keyStorePath; _keyStorePassword = keyStorePassword; - if (_keyStorePassword.equals("none")) + if (_keyStorePassword != null && _keyStorePassword.equals("none")) { _keyStorePassword = null; } @@ -113,29 +116,63 @@ public class SSLContextFactory { } } + public SSLContextFactory(String trustStorePath, String trustStorePassword, String trustStoreCertType, + KeyManager customKeyManager) + { + + _trustStorePath = trustStorePath; + _trustStorePassword = trustStorePassword; + + if (_trustStorePassword != null && _trustStorePassword.equals("none")) + { + _trustStorePassword = null; + } + _trustStoreCertType = trustStoreCertType; + + if (_trustStorePath == null) { + throw new IllegalArgumentException("A TrustStore path or KeyStore path must be specified"); + } + if (_trustStoreCertType == null) { + throw new IllegalArgumentException("Cert type must be specified"); + } + + this.customKeyManager = customKeyManager; + } + + /** * Builds a SSLContext appropriate for use with a server * @return SSLContext * @throws GeneralSecurityException * @throws IOException */ + public SSLContext buildServerContext() throws GeneralSecurityException, IOException { - // Create keystore - KeyStore ks = getInitializedKeyStore(_keyStorePath,_keyStorePassword); - - // Set up key manager factory to use our key store - KeyManagerFactory kmf = KeyManagerFactory.getInstance(_keyStoreCertType); - kmf.init(ks, _keyStorePassword.toCharArray()); - - KeyStore ts = getInitializedKeyStore(_trustStorePath,_trustStorePassword); + KeyStore ts = SSLUtil.getInitializedKeyStore(_trustStorePath,_trustStorePassword); TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType); tmf.init(ts); // Initialize the SSLContext to work with our key managers. - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + SSLContext sslContext = SSLContext.getInstance("TLS"); + + if (customKeyManager != null) + { + sslContext.init(new KeyManager[]{customKeyManager}, + tmf.getTrustManagers(), null); + + } + else + { + // Create keystore + KeyStore ks = SSLUtil.getInitializedKeyStore(_keyStorePath,_keyStorePassword); + // Set up key manager factory to use our key store + KeyManagerFactory kmf = KeyManagerFactory.getInstance(_keyStoreCertType); + kmf.init(ks, _keyStorePassword.toCharArray()); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } + return sslContext; } @@ -147,7 +184,7 @@ public class SSLContextFactory { */ public SSLContext buildClientContext() throws GeneralSecurityException, IOException { - KeyStore ks = getInitializedKeyStore(_trustStorePath,_trustStorePassword); + KeyStore ks = SSLUtil.getInitializedKeyStore(_trustStorePath,_trustStorePassword); TrustManagerFactory tmf = TrustManagerFactory.getInstance(_trustStoreCertType); tmf.init(ks); SSLContext context = SSLContext.getInstance("TLS"); @@ -155,41 +192,4 @@ public class SSLContextFactory { return context; } - private KeyStore getInitializedKeyStore(String storePath, String storePassword) throws GeneralSecurityException, IOException - { - KeyStore ks = KeyStore.getInstance("JKS"); - InputStream in = null; - try - { - File f = new File(storePath); - if (f.exists()) - { - in = new FileInputStream(f); - } - else - { - in = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath); - } - if (in == null) - { - throw new IOException("Unable to load keystore resource: " + storePath); - } - ks.load(in, storePassword.toCharArray()); - } - finally - { - if (in != null) - { - //noinspection EmptyCatchBlock - try - { - in.close(); - } - catch (IOException ignored) - { - } - } - } - return ks; - } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java index 74064c9d11..d5f97f48a8 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java @@ -181,10 +181,25 @@ public class ClientDelegate extends ConnectionDelegate @Override public void connectionOpenOk(Connection conn, ConnectionOpenOk ok) { SaslClient sc = conn.getSaslClient(); - if (sc != null && sc.getMechanismName().equals("GSSAPI") && getUserID() != null) + if (sc != null) { - conn.setUserID(getUserID()); + if (sc.getMechanismName().equals("GSSAPI")) + { + String id = getKerberosUser(); + if (id != null) + { + conn.setUserID(id); + } + } + else if (sc.getMechanismName().equals("EXTERNAL")) + { + if (conn.getSecurityLayer() != null) + { + conn.setUserID(conn.getSecurityLayer().getUserID()); + } + } } + conn.setState(OPEN); } @@ -245,7 +260,7 @@ public class ClientDelegate extends ConnectionDelegate } - private String getUserID() + private String getKerberosUser() { log.debug("Obtaining userID from kerberos"); String service = conSettings.getSaslProtocol() + "@" + conSettings.getSaslServerName(); 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 8c2da9d77a..2ca5d28f42 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 @@ -25,14 +25,7 @@ import static org.apache.qpid.transport.Connection.State.CLOSING; import static org.apache.qpid.transport.Connection.State.NEW; import static org.apache.qpid.transport.Connection.State.OPEN; import static org.apache.qpid.transport.Connection.State.OPENING; -import org.apache.qpid.transport.network.ConnectionBinding; -import org.apache.qpid.transport.network.io.IoTransport; -import org.apache.qpid.transport.util.Logger; -import org.apache.qpid.transport.util.Waiter; -import org.apache.qpid.util.Strings; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslServer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -40,6 +33,14 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslServer; + +import org.apache.qpid.transport.network.security.SecurityLayer; +import org.apache.qpid.transport.util.Logger; +import org.apache.qpid.transport.util.Waiter; +import org.apache.qpid.util.Strings; + /** * Connection @@ -109,7 +110,8 @@ public class Connection extends ConnectionInvoker private Map<String,Object> _serverProperties; private String userID; private ConnectionSettings conSettings; - + private SecurityLayer securityLayer; + // want to make this final private int _connectionId; @@ -215,10 +217,17 @@ public class Connection extends ConnectionInvoker userID = settings.getUsername(); delegate = new ClientDelegate(settings); - IoTransport.connect(settings.getHost(), + /*IoTransport.connect(settings.getHost(), settings.getPort(), ConnectionBinding.get(this), - settings.isUseSSL()); + settings.isUseSSL());*/ + + TransportBuilder transport = new TransportBuilder(); + transport.init(this); + this.sender = transport.buildSenderPipe(); + transport.buildReceiverPipe(this); + this.securityLayer = transport.getSecurityLayer(); + send(new ProtocolHeader(1, 0, 10)); Waiter w = new Waiter(lock, timeout); @@ -633,5 +642,10 @@ public class Connection extends ConnectionInvoker { return conSettings; } + + public SecurityLayer getSecurityLayer() + { + return securityLayer; + } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java index c063ef5e6f..08678b213b 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionSettings.java @@ -22,6 +22,12 @@ package org.apache.qpid.transport; import java.util.Map; +/** + * A ConnectionSettings object can only be associated with + * one Connection object. I have added an assertion that will + * throw an exception if it is used by more than on Connection + * + */ public class ConnectionSettings { String protocol = "tcp"; @@ -29,18 +35,34 @@ public class ConnectionSettings String vhost; String username = "guest"; String password = "guest"; - String saslMechs = "PLAIN"; - String saslProtocol = "AMQP"; - String saslServerName = "localhost"; int port = 5672; + boolean tcpNodelay = Boolean.getBoolean("amqj.tcp_nodelay"); int maxChannelCount = 32767; int maxFrameSize = 65535; int heartbeatInterval; + int readBufferSize = 65535; + int writeBufferSize = 65535; + long transportTimeout = 60000; + + // SSL props boolean useSSL; + String keyStorePath = System.getProperty("javax.net.ssl.keyStore"); + String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword"); + String keyStoreCertType = System.getProperty("qpid.ssl.keyStoreCertType","SunX509");; + String trustStoreCertType = System.getProperty("qpid.ssl.trustStoreCertType","SunX509");; + String trustStorePath = System.getProperty("javax.net.ssl.trustStore");; + String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");; + String certAlias; + boolean verifyHostname; + + // SASL props + String saslMechs = System.getProperty("qpid.sasl_mechs", "PLAIN"); + String saslProtocol = System.getProperty("qpid.sasl_protocol", "AMQP"); + String saslServerName = System.getProperty("qpid.sasl_server_name", "localhost"); boolean useSASLEncryption; - boolean tcpNodelay; + private Map<String, Object> _clientProperties; - + public boolean isTcpNodelay() { return tcpNodelay; @@ -200,4 +222,115 @@ public class ConnectionSettings { return _clientProperties; } + + public String getKeyStorePath() + { + return keyStorePath; + } + + public void setKeyStorePath(String keyStorePath) + { + this.keyStorePath = keyStorePath; + } + + public String getKeyStorePassword() + { + return keyStorePassword; + } + + public void setKeyStorePassword(String keyStorePassword) + { + this.keyStorePassword = keyStorePassword; + } + + public String getTrustStorePath() + { + return trustStorePath; + } + + public void setTrustStorePath(String trustStorePath) + { + this.trustStorePath = trustStorePath; + } + + public String getTrustStorePassword() + { + return trustStorePassword; + } + + public void setTrustStorePassword(String trustStorePassword) + { + this.trustStorePassword = trustStorePassword; + } + + public String getCertAlias() + { + return certAlias; + } + + public void setCertAlias(String certAlias) + { + this.certAlias = certAlias; + } + + public boolean isVerifyHostname() + { + return verifyHostname; + } + + public void setVerifyHostname(boolean verifyHostname) + { + this.verifyHostname = verifyHostname; + } + + public String getKeyStoreCertType() + { + return keyStoreCertType; + } + + public void setKeyStoreCertType(String keyStoreCertType) + { + this.keyStoreCertType = keyStoreCertType; + } + + public String getTrustStoreCertType() + { + return trustStoreCertType; + } + + public void setTrustStoreCertType(String trustStoreCertType) + { + this.trustStoreCertType = trustStoreCertType; + } + + public int getReadBufferSize() + { + return readBufferSize; + } + + public void setReadBufferSize(int readBufferSize) + { + this.readBufferSize = readBufferSize; + } + + public int getWriteBufferSize() + { + return writeBufferSize; + } + + public void setWriteBufferSize(int writeBufferSize) + { + this.writeBufferSize = writeBufferSize; + } + + public long getTransportTimeout() + { + return transportTimeout; + } + + public void setTransportTimeout(long transportTimeout) + { + this.transportTimeout = transportTimeout; + } + } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportBuilder.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportBuilder.java new file mode 100644 index 0000000000..d1ae95a3bb --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportBuilder.java @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.transport; + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.network.Assembler; +import org.apache.qpid.transport.network.Disassembler; +import org.apache.qpid.transport.network.InputHandler; +import org.apache.qpid.transport.network.NetworkTransport; +import org.apache.qpid.transport.network.Transport; +import org.apache.qpid.transport.network.security.SecurityLayer; + +public class TransportBuilder +{ + private Connection con; + private ConnectionSettings settings; + private NetworkTransport transport; + private SecurityLayer securityLayer = new SecurityLayer(); + + public void init(Connection con) throws TransportException + { + this.con = con; + this.settings = con.getConnectionSettings(); + transport = Transport.getTransport(); + transport.init(settings); + securityLayer.init(con); + } + + public Sender<ProtocolEvent> buildSenderPipe() + { + ConnectionSettings settings = con.getConnectionSettings(); + + // Io layer + Sender<ByteBuffer> sender = transport.sender(); + + // Security layer + sender = securityLayer.sender(sender); + + Disassembler dis = new Disassembler(sender, settings.getMaxFrameSize()); + return dis; + } + + public void buildReceiverPipe(Receiver<ProtocolEvent> delegate) + { + ConnectionSettings settings = con.getConnectionSettings(); + + Receiver<ByteBuffer> receiver = new InputHandler(new Assembler(delegate)); + + // Security layer + receiver = securityLayer.receiver(receiver); + + //Io layer + transport.receiver(receiver); + } + + public SecurityLayer getSecurityLayer() + { + return securityLayer; + } + +}
\ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkTransport.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkTransport.java new file mode 100644 index 0000000000..5e12d7e7c6 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkTransport.java @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.transport.network; + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Receiver; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.ConnectionSettings; + +public interface NetworkTransport +{ + public void init(ConnectionSettings settings); + + public Sender<ByteBuffer> sender(); + + public void receiver(Receiver<ByteBuffer> delegate); + + public void close(); +}
\ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Transport.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Transport.java new file mode 100644 index 0000000000..f0bf04d04f --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Transport.java @@ -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. + * + */ + +package org.apache.qpid.transport.network; + +import org.apache.qpid.transport.TransportException; + +public class Transport +{ + private final static Class<?> transportClass; + + static + { + try + { + transportClass = + Class.forName(System.getProperty("qpid.transport", + "org.apache.qpid.transport.network.io.IoNetworkTransport")); + + } + catch(Exception e) + { + throw new Error("Error occured while loading Qpid Transport",e); + } + } + + public static NetworkTransport getTransport() throws TransportException + { + try + { + return (NetworkTransport)transportClass.newInstance(); + } + catch (Exception e) + { + throw new TransportException("Error while creating a new transport instance",e); + } + } +}
\ No newline at end of file diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoContext.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoContext.java new file mode 100644 index 0000000000..72520c64ef --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoContext.java @@ -0,0 +1,15 @@ +package org.apache.qpid.transport.network.io; + +import java.net.Socket; +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Sender; + +public interface IoContext +{ + Sender<ByteBuffer> getSender(); + + IoReceiver getReceiver(); + + Socket getSocket(); +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java new file mode 100644 index 0000000000..4e6d2130ae --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoNetworkTransport.java @@ -0,0 +1,120 @@ +/* +* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.transport.network.io; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.ConnectionSettings; +import org.apache.qpid.transport.Receiver; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.TransportException; +import org.apache.qpid.transport.network.NetworkTransport; +import org.apache.qpid.transport.util.Logger; + +public class IoNetworkTransport implements NetworkTransport, IoContext +{ + static + { + org.apache.mina.common.ByteBuffer.setAllocator + (new org.apache.mina.common.SimpleByteBufferAllocator()); + org.apache.mina.common.ByteBuffer.setUseDirectBuffers + (Boolean.getBoolean("amqj.enableDirectBuffers")); + } + + private static final Logger log = Logger.get(IoNetworkTransport.class); + + private Socket socket; + private Sender<ByteBuffer> sender; + private IoReceiver receiver; + private long timeout = 60000; + private ConnectionSettings settings; + + @Override + public void init(ConnectionSettings settings) + { + try + { + this.settings = settings; + InetAddress address = InetAddress.getByName(settings.getHost()); + socket = new Socket(); + socket.setReuseAddress(true); + socket.setTcpNoDelay(settings.isTcpNodelay()); + + log.debug("default-SO_RCVBUF : %s", socket.getReceiveBufferSize()); + log.debug("default-SO_SNDBUF : %s", socket.getSendBufferSize()); + + socket.setSendBufferSize(settings.getWriteBufferSize()); + socket.setReceiveBufferSize(settings.getReadBufferSize()); + + log.debug("new-SO_RCVBUF : %s", socket.getReceiveBufferSize()); + log.debug("new-SO_SNDBUF : %s", socket.getSendBufferSize()); + + socket.connect(new InetSocketAddress(address, settings.getPort())); + } + catch (SocketException e) + { + throw new TransportException("Error connecting to broker", e); + } + catch (IOException e) + { + throw new TransportException("Error connecting to broker", e); + } + } + + @Override + public void receiver(Receiver<ByteBuffer> delegate) + { + receiver = new IoReceiver(this, delegate, + 2*settings.getReadBufferSize() , timeout); + } + + @Override + public Sender<ByteBuffer> sender() + { + return new IoSender(this, 2*settings.getWriteBufferSize(), timeout); + } + + @Override + public void close() + { + + } + + public Sender<ByteBuffer> getSender() + { + return sender; + } + + public IoReceiver getReceiver() + { + return receiver; + } + + public Socket getSocket() + { + return socket; + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java index e0e06d22ec..19a683d505 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java @@ -42,7 +42,7 @@ final class IoReceiver implements Runnable private static final Logger log = Logger.get(IoReceiver.class); - private final IoTransport transport; + private final IoContext ioCtx; private final Receiver<ByteBuffer> receiver; private final int bufferSize; private final Socket socket; @@ -52,13 +52,13 @@ final class IoReceiver implements Runnable private final boolean shutdownBroken = ((String) System.getProperties().get("os.name")).matches("(?i).*windows.*"); - public IoReceiver(IoTransport transport, Receiver<ByteBuffer> receiver, + public IoReceiver(IoContext ioCtx, Receiver<ByteBuffer> receiver, int bufferSize, long timeout) { - this.transport = transport; + this.ioCtx = ioCtx; this.receiver = receiver; this.bufferSize = bufferSize; - this.socket = transport.getSocket(); + this.socket = ioCtx.getSocket(); this.timeout = timeout; try diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java index 383fd6131a..66b97e8225 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java @@ -43,7 +43,7 @@ public final class IoSender implements Runnable, Sender<ByteBuffer> // we can test other cases as well private final static int START = Integer.MAX_VALUE - 10; - private final IoTransport transport; + private final IoContext ioCtx; private final long timeout; private final Socket socket; private final OutputStream out; @@ -60,10 +60,10 @@ public final class IoSender implements Runnable, Sender<ByteBuffer> private volatile Throwable exception = null; - public IoSender(IoTransport transport, int bufferSize, long timeout) + public IoSender(IoContext ioCtx, int bufferSize, long timeout) { - this.transport = transport; - this.socket = transport.getSocket(); + this.ioCtx = ioCtx; + this.socket = ioCtx.getSocket(); this.buffer = new byte[pof2(bufferSize)]; // buffer size must be a power of 2 this.timeout = timeout; @@ -207,7 +207,7 @@ public final class IoSender implements Runnable, Sender<ByteBuffer> throw new SenderException("join timed out"); } } - transport.getReceiver().close(false); + ioCtx.getReceiver().close(false); } catch (InterruptedException e) { diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java index b648ba4858..bfdbb34978 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java @@ -38,8 +38,8 @@ import org.apache.qpid.transport.Receiver; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.TransportException; import org.apache.qpid.transport.network.ConnectionBinding; -import org.apache.qpid.transport.network.ssl.SSLReceiver; -import org.apache.qpid.transport.network.ssl.SSLSender; +import org.apache.qpid.transport.network.security.ssl.SSLReceiver; +import org.apache.qpid.transport.network.security.ssl.SSLSender; import org.apache.qpid.transport.util.Logger; /** @@ -51,7 +51,7 @@ import org.apache.qpid.transport.util.Logger; * SO_RCVBUF - amqj.receiveBufferSize * SO_SNDBUF - amqj.sendBufferSize */ -public final class IoTransport<E> +public final class IoTransport<E> implements IoContext { static @@ -119,17 +119,17 @@ public final class IoTransport<E> } } - Sender<ByteBuffer> getSender() + public Sender<ByteBuffer> getSender() { return sender; } - IoReceiver getReceiver() + public IoReceiver getReceiver() { return receiver; } - Socket getSocket() + public Socket getSocket() { return socket; } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayer.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayer.java new file mode 100644 index 0000000000..3f0966903d --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/SecurityLayer.java @@ -0,0 +1,185 @@ +/* +* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.transport.network.security; + +import java.nio.ByteBuffer; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionListener; +import org.apache.qpid.transport.ConnectionSettings; +import org.apache.qpid.transport.Receiver; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.TransportException; +import org.apache.qpid.transport.network.security.sasl.SASLReceiver; +import org.apache.qpid.transport.network.security.sasl.SASLSender; +import org.apache.qpid.transport.network.security.ssl.SSLReceiver; +import org.apache.qpid.transport.network.security.ssl.SSLSender; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; + +public class SecurityLayer +{ + ConnectionSettings settings; + Connection con; + SSLSecurityLayer sslLayer; + SASLSecurityLayer saslLayer; + + public void init(Connection con) throws TransportException + { + this.con = con; + this.settings = con.getConnectionSettings(); + if (settings.isUseSSL()) + { + sslLayer = new SSLSecurityLayer(); + } + if (settings.isUseSASLEncryption()) + { + saslLayer = new SASLSecurityLayer(); + } + + } + + public Sender<ByteBuffer> sender(Sender<ByteBuffer> delegate) + { + Sender<ByteBuffer> sender = delegate; + + if (settings.isUseSSL()) + { + sender = sslLayer.sender(sender); + } + + if (settings.isUseSASLEncryption()) + { + sender = saslLayer.sender(sender); + } + + return sender; + } + + public Receiver<ByteBuffer> receiver(Receiver<ByteBuffer> delegate) + { + Receiver<ByteBuffer> receiver = delegate; + + if (settings.isUseSSL()) + { + receiver = sslLayer.receiver(receiver); + } + + if (settings.isUseSASLEncryption()) + { + receiver = saslLayer.receiver(receiver); + } + + return receiver; + } + + public String getUserID() + { + if (settings.isUseSSL()) + { + return sslLayer.getUserID(); + } + else + { + return null; + } + } + + class SSLSecurityLayer + { + SSLEngine engine; + SSLSender sender; + + public SSLSecurityLayer() + { + SSLContext sslCtx; + try + { + sslCtx = SSLUtil.createSSLContext(settings); + } + catch (Exception e) + { + throw new TransportException("Error creating SSL Context", e); + } + + try + { + engine = sslCtx.createSSLEngine(); + engine.setUseClientMode(true); + } + catch(Exception e) + { + throw new TransportException("Error creating SSL Engine", e); + } + } + + public SSLSender sender(Sender<ByteBuffer> delegate) + { + sender = new SSLSender(engine,delegate); + sender.setConnectionSettings(settings); + return sender; + } + + public SSLReceiver receiver(Receiver<ByteBuffer> delegate) + { + if (sender == null) + { + throw new + IllegalStateException("SecurityLayer.sender method should be " + + "invoked before SecurityLayer.receiver"); + } + + SSLReceiver receiver = new SSLReceiver(engine,delegate,sender); + receiver.setConnectionSettings(settings); + return receiver; + } + + public String getUserID() + { + return SSLUtil.retriveIdentity(engine); + } + + } + + class SASLSecurityLayer + { + public SASLSecurityLayer() + { + } + + public SASLSender sender(Sender<ByteBuffer> delegate) + { + SASLSender sender = new SASLSender(delegate); + con.addConnectionListener((ConnectionListener)sender); + return sender; + } + + public SASLReceiver receiver(Receiver<ByteBuffer> delegate) + { + SASLReceiver receiver = new SASLReceiver(delegate); + con.addConnectionListener((ConnectionListener)receiver); + return receiver; + } + + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java new file mode 100644 index 0000000000..2a3aba9a95 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/QpidClientX509KeyManager.java @@ -0,0 +1,80 @@ +package org.apache.qpid.transport.network.security.ssl; + +import java.net.Socket; +import java.security.KeyStore; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedKeyManager; + +import org.apache.qpid.transport.util.Logger; + +public class QpidClientX509KeyManager extends X509ExtendedKeyManager +{ + private static final Logger log = Logger.get(QpidClientX509KeyManager.class); + + X509ExtendedKeyManager delegate; + String alias; + + public QpidClientX509KeyManager(String alias, String keyStorePath, + String keyStorePassword,String keyStoreCertType) throws Exception + { + this.alias = alias; + KeyStore ks = SSLUtil.getInitializedKeyStore(keyStorePath,keyStorePassword); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyStoreCertType); + kmf.init(ks, keyStorePassword.toCharArray()); + this.delegate = (X509ExtendedKeyManager)kmf.getKeyManagers()[0]; + } + + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) + { + log.debug("chooseClientAlias:Returning alias " + alias); + return alias; + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) + { + return delegate.chooseServerAlias(keyType, issuers, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) + { + return delegate.getCertificateChain(alias); + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) + { + log.debug("getClientAliases:Returning alias " + alias); + return new String[]{alias}; + } + + @Override + public PrivateKey getPrivateKey(String alias) + { + return delegate.getPrivateKey(alias); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) + { + return delegate.getServerAliases(keyType, issuers); + } + + public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) + { + log.debug("chooseEngineClientAlias:Returning alias " + alias); + return alias; + } + + public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) + { + return delegate.chooseEngineServerAlias(keyType, issuers, engine); + } +} diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLReceiver.java index e6e6c5f791..082ae9e8ec 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLReceiver.java @@ -18,7 +18,7 @@ * under the License. * */ -package org.apache.qpid.transport.network.ssl; +package org.apache.qpid.transport.network.security.ssl; import java.nio.ByteBuffer; @@ -28,6 +28,7 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; +import org.apache.qpid.transport.ConnectionSettings; import org.apache.qpid.transport.Receiver; import org.apache.qpid.transport.TransportException; import org.apache.qpid.transport.util.Logger; @@ -42,7 +43,8 @@ public class SSLReceiver implements Receiver<ByteBuffer> private ByteBuffer localBuffer; private boolean dataCached = false; private final Object notificationToken; - + private ConnectionSettings settings; + private static final Logger log = Logger.get(SSLReceiver.class); public SSLReceiver(SSLEngine engine, Receiver<ByteBuffer> delegate,SSLSender sender) @@ -56,6 +58,11 @@ public class SSLReceiver implements Receiver<ByteBuffer> notificationToken = sender.getNotificationToken(); } + public void setConnectionSettings(ConnectionSettings settings) + { + this.settings = settings; + } + public void closed() { delegate.closed(); @@ -159,8 +166,13 @@ public class SSLReceiver implements Receiver<ByteBuffer> sender.doTasks(); handshakeStatus = engine.getHandshakeStatus(); - case NEED_WRAP: case FINISHED: + if (this.settings != null && this.settings.isVerifyHostname() ) + { + SSLUtil.verifyHostname(engine, this.settings.getHost()); + } + + case NEED_WRAP: case NOT_HANDSHAKING: synchronized(notificationToken) { diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLSender.java index bd5662a5fb..24cedcc75a 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLSender.java @@ -17,7 +17,7 @@ * under the License. * */ -package org.apache.qpid.transport.network.ssl; +package org.apache.qpid.transport.network.security.ssl; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,6 +28,7 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; +import org.apache.qpid.transport.ConnectionSettings; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.SenderException; import org.apache.qpid.transport.util.Logger; @@ -39,7 +40,8 @@ public class SSLSender implements Sender<ByteBuffer> private int sslBufSize; private ByteBuffer netData; private long timeout = 30000; - + private ConnectionSettings settings; + private final Object engineState = new Object(); private final AtomicBoolean closed = new AtomicBoolean(false); @@ -53,6 +55,11 @@ public class SSLSender implements Sender<ByteBuffer> netData = ByteBuffer.allocate(sslBufSize); timeout = Long.getLong("qpid.ssl_timeout", 60000); } + + public void setConnectionSettings(ConnectionSettings settings) + { + this.settings = settings; + } public void close() { @@ -225,6 +232,11 @@ public class SSLSender implements Sender<ByteBuffer> break; case FINISHED: + if (this.settings != null && this.settings.isVerifyHostname() ) + { + SSLUtil.verifyHostname(engine, this.settings.getHost()); + } + case NOT_HANDSHAKING: break; //do nothing diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java new file mode 100644 index 0000000000..6c5c56a175 --- /dev/null +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/network/security/ssl/SSLUtil.java @@ -0,0 +1,177 @@ +package org.apache.qpid.transport.network.security.ssl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLPeerUnverifiedException; + +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.ConnectionSettings; +import org.apache.qpid.transport.TransportException; +import org.apache.qpid.transport.util.Logger; + +public class SSLUtil +{ + private static final Logger log = Logger.get(SSLUtil.class); + + public static void verifyHostname(SSLEngine engine,String hostnameExpected) + { + try + { + Certificate cert = engine.getSession().getPeerCertificates()[0]; + Principal p = ((X509Certificate)cert).getSubjectDN(); + String dn = p.getName(); + String hostname = null; + + if (dn.contains("CN=")) + { + hostname = dn.substring(3, + dn.indexOf(",") == -1? dn.length(): dn.indexOf(",")); + } + + if (log.isDebugEnabled()) + { + log.debug("Hostname expected : " + hostnameExpected); + log.debug("Distinguished Name for server certificate : " + dn); + log.debug("Host Name obtained from DN : " + hostname); + } + + if (hostname != null && !(hostname.equalsIgnoreCase(hostnameExpected) || + hostname.equalsIgnoreCase(hostnameExpected + ".localdomain"))) + { + throw new TransportException("SSL hostname verification failed." + + " Expected : " + hostnameExpected + + " Found in cert : " + hostname); + } + + } + catch(SSLPeerUnverifiedException e) + { + log.warn("Exception received while trying to verify hostname",e); + // For some reason the SSL engine sets the handshake status to FINISH twice + // in succession. The first time the peer certificate + // info is not available. The second time it works ! + // Therefore have no choice but to ignore the exception here. + } + } + + public static String retriveIdentity(SSLEngine engine) + { + StringBuffer id = new StringBuffer(); + try + { + Certificate cert = engine.getSession().getLocalCertificates()[0]; + Principal p = ((X509Certificate)cert).getSubjectDN(); + String dn = p.getName(); + + if (dn.contains("CN=")) + { + id.append(dn.substring(3, + dn.indexOf(",") == -1? dn.length(): dn.indexOf(","))); + } + + if (dn.contains("DC=")) + { + id.append("@"); + int c = 0; + for (String toks : dn.split(",")) + { + if (toks.contains("DC")) + { + if (c > 0) {id.append(".");} + id.append(toks.substring( + toks.indexOf("=")+1, + toks.indexOf(",") == -1? toks.length(): toks.indexOf(","))); + c++; + } + } + } + } + catch(Exception e) + { + log.info("Exception received while trying to retrive client identity from SSL cert",e); + } + + log.debug("Extracted Identity from client certificate : " + id); + return id.toString(); + } + + public static SSLContext createSSLContext(ConnectionSettings settings) throws Exception + { + SSLContextFactory sslContextFactory; + + if (settings.getCertAlias() == null) + { + sslContextFactory = + new SSLContextFactory(settings.getTrustStorePath(), + settings.getTrustStorePassword(), + settings.getTrustStoreCertType(), + settings.getKeyStorePath(), + settings.getKeyStorePassword(), + settings.getKeyStoreCertType()); + + } else + { + sslContextFactory = + new SSLContextFactory(settings.getTrustStorePath(), + settings.getTrustStorePassword(), + settings.getTrustStoreCertType(), + new QpidClientX509KeyManager(settings.getCertAlias(), + settings.getKeyStorePath(), + settings.getKeyStorePassword(), + settings.getKeyStoreCertType())); + + log.debug("Using custom key manager"); + } + + return sslContextFactory.buildServerContext(); + + } + + public static KeyStore getInitializedKeyStore(String storePath, String storePassword) throws GeneralSecurityException, IOException + { + KeyStore ks = KeyStore.getInstance("JKS"); + InputStream in = null; + try + { + File f = new File(storePath); + if (f.exists()) + { + in = new FileInputStream(f); + } + else + { + in = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath); + } + if (in == null) + { + throw new IOException("Unable to load keystore resource: " + storePath); + } + ks.load(in, storePassword.toCharArray()); + } + finally + { + if (in != null) + { + //noinspection EmptyCatchBlock + try + { + in.close(); + } + catch (IOException ignored) + { + } + } + } + return ks; + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java new file mode 100644 index 0000000000..74326c02ec --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java @@ -0,0 +1,161 @@ +package org.apache.qpid.client.ssl; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import javax.jms.Session; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTestConnection_0_10; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.transport.Connection; + +public class SSLTest extends QpidTestCase +{ + + @Override + protected void setUp() throws Exception + { + System.setProperty("javax.net.debug", "ssl"); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception + { + System.setProperty("javax.net.debug", ""); + super.tearDown(); + } + + public void testCreateSSLContextFromConnectionURLParams() + { + if (Boolean.getBoolean("profile.use_ssl")) + { + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:%s" + + "?ssl='true'&ssl_verify_hostname='true'" + + "&key_store='%s'&key_store_password='%s'" + + "&trust_store='%s'&trust_store_password='%s'" + + "'"; + + String keyStore = System.getProperty("javax.net.ssl.keyStore"); + String keyStorePass = System.getProperty("javax.net.ssl.keyStorePassword"); + String trustStore = System.getProperty("javax.net.ssl.trustStore"); + String trustStorePass = System.getProperty("javax.net.ssl.trustStorePassword"); + + url = String.format(url,System.getProperty("test.port.ssl"), + keyStore,keyStorePass,trustStore,trustStorePass); + + // temporarily set the trust/key store jvm args to something else + // to ensure we only read from the connection URL param. + System.setProperty("javax.net.ssl.trustStore","fessgsdgd"); + System.setProperty("javax.net.ssl.trustStorePassword","fessgsdgd"); + System.setProperty("javax.net.ssl.keyStore","fessgsdgd"); + System.setProperty("javax.net.ssl.keyStorePassword","fessgsdgd"); + try + { + AMQConnection con = new AMQConnection(url); + Session ssn = con.createSession(false,Session.AUTO_ACKNOWLEDGE); + } + catch (Exception e) + { + fail("SSL Connection should be successful"); + } + finally + { + System.setProperty("javax.net.ssl.trustStore",trustStore); + System.setProperty("javax.net.ssl.trustStorePassword",trustStorePass); + System.setProperty("javax.net.ssl.keyStore",keyStore); + System.setProperty("javax.net.ssl.keyStorePassword",keyStorePass); + } + } + } + + public void testMultipleCertsInSingleStore() throws Exception + { + if (Boolean.getBoolean("profile.use_ssl")) + { + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_cert_alias='app1''"; + + AMQTestConnection_0_10 con = new AMQTestConnection_0_10(url); + Connection transportCon = con.getConnection(); + String userID = transportCon.getSecurityLayer().getUserID(); + assertEquals("The correct certificate was not choosen","app1@acme.org",userID); + con.close(); + + url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_cert_alias='app2''"; + + con = new AMQTestConnection_0_10(url); + transportCon = con.getConnection(); + userID = transportCon.getSecurityLayer().getUserID(); + assertEquals("The correct certificate was not choosen","app2@acme.org",userID); + con.close(); + } + } + + public void testVerifyHostName() + { + if (Boolean.getBoolean("profile.use_ssl")) + { + String url = "amqp://guest:guest@test/?brokerlist='tcp://127.0.0.1:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_verify_hostname='true''"; + + try + { + AMQConnection con = new AMQConnection(url); + fail("Hostname verification failed. No exception was thrown"); + } + catch (Exception e) + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bout)); + String strace = bout.toString(); + assertTrue("Correct exception not thrown",strace.contains("SSL hostname verification failed")); + } + + } + } + + public void testVerifyLocalHost() + { + if (Boolean.getBoolean("profile.use_ssl")) + { + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_verify_hostname='true''"; + + try + { + AMQConnection con = new AMQConnection(url); + } + catch (Exception e) + { + fail("Hostname verification should succeed"); + } + } + } + + public void testVerifyLocalHostLocalDomain() + { + if (Boolean.getBoolean("profile.use_ssl")) + { + String url = "amqp://guest:guest@test/?brokerlist='tcp://localhost.localdomain:" + + System.getProperty("test.port.ssl") + + "?ssl='true'&ssl_verify_hostname='true''"; + + try + { + AMQConnection con = new AMQConnection(url); + } + catch (Exception e) + { + fail("Hostname verification should succeed"); + } + + } + } +} 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 aeddfd00fe..732a28553c 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 @@ -77,7 +77,7 @@ public class AddressBasedDestinationTest extends QpidTestCase } catch(JMSException e) { - assertTrue(e.getMessage().contains("The name supplied in the address " + + assertTrue(e.getMessage().contains("The name 'testQueue1' supplied in the address " + "doesn't resolve to an exchange or a queue")); } @@ -140,7 +140,7 @@ public class AddressBasedDestinationTest extends QpidTestCase } catch(JMSException e) { - assertTrue(e.getMessage().contains("The name supplied in the address " + + assertTrue(e.getMessage().contains("The name 'testQueue3' supplied in the address " + "doesn't resolve to an exchange or a queue")); } @@ -150,7 +150,7 @@ public class AddressBasedDestinationTest extends QpidTestCase } catch(JMSException e) { - assertTrue(e.getMessage().contains("The name supplied in the address " + + assertTrue(e.getMessage().contains("The name 'testQueue3' supplied in the address " + "doesn't resolve to an exchange or a queue")); } @@ -167,7 +167,7 @@ public class AddressBasedDestinationTest extends QpidTestCase } catch(JMSException e) { - assertTrue(e.getMessage().contains("The name supplied in the address " + + assertTrue(e.getMessage().contains("The name 'testQueue3' supplied in the address " + "doesn't resolve to an exchange or a queue")); } assertFalse("Queue should not be created",( diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java index 91bb5d2529..cafd212dd3 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java @@ -334,6 +334,7 @@ public class DurableSubscriptionTest extends QpidTestCase { _logger.info("Receive message on consumer 3 :expecting B"); msg = consumer3.receive(POSITIVE_RECEIVE_TIMEOUT); + assertNotNull(msg); assertEquals("B", ((TextMessage) msg).getText()); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/FaultTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/FaultTest.java index 1e5932b6db..47705f8105 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/FaultTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/FaultTest.java @@ -339,7 +339,7 @@ public class FaultTest extends AbstractXATestCase { assertEquals("Wrong error code: ", XAException.XAER_PROTO, e.errorCode); } - } + } /** * Strategy: @@ -355,7 +355,7 @@ public class FaultTest extends AbstractXATestCase _xaResource.end(xid, XAResource.TMSUCCESS); xid = getNewXid(); _xaResource.start(xid, XAResource.TMNOFLAGS); - assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 0); + assertEquals("Wrong timeout", _xaResource.getTransactionTimeout(), 1000); } /** @@ -381,5 +381,29 @@ public class FaultTest extends AbstractXATestCase assertEquals("Wrong error code: ", XAException.XA_RBTIMEOUT, e.errorCode); } } + + /** + * Strategy: + * Set the transaction timeout to 1000 + */ + public void testTransactionTimeoutAfterCommit() throws Exception + { + Xid xid = getNewXid(); + + _xaResource.start(xid, XAResource.TMNOFLAGS); + _xaResource.setTransactionTimeout(1000); + assertEquals("Wrong timeout", 1000,_xaResource.getTransactionTimeout()); + + //_xaResource.prepare(xid); + _xaResource.end(xid, XAResource.TMSUCCESS); + _xaResource.commit(xid, true); + + _xaResource.setTransactionTimeout(2000); + assertEquals("Wrong timeout", 2000,_xaResource.getTransactionTimeout()); + + xid = getNewXid(); + _xaResource.start(xid, XAResource.TMNOFLAGS); + assertEquals("Wrong timeout", 2000, _xaResource.getTransactionTimeout()); + } } diff --git a/qpid/java/test-profiles/CPPExcludes b/qpid/java/test-profiles/CPPExcludes index ec3649fe98..d38e895ee9 100755 --- a/qpid/java/test-profiles/CPPExcludes +++ b/qpid/java/test-profiles/CPPExcludes @@ -56,10 +56,13 @@ org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* // QPID-1262, QPID-1119 : This test fails occasionally due to potential protocol issue. org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* -// c++ broker doesn't support priorities, TTL or message bouncing +// c++ broker doesn't support priorities, message bouncing org.apache.qpid.server.exchange.ReturnUnroutableMandatoryMessageTest#* org.apache.qpid.server.queue.PriorityTest#* + +// c++ broker expires messages on delivery or when the queue cleaner thread runs. org.apache.qpid.server.queue.TimeToLiveTest#testActiveTTL +org.apache.qpid.server.queue.TimeToLiveTest#testActiveTTLwithDurableSubscription // QPID-1727 , QPID-1726 :c++ broker does not support flow to disk on transient queues. Also it requries a persistent store impl. for Apache org.apache.qpid.test.client.QueueBrowsingFlowToDiskTest#* diff --git a/qpid/java/test-profiles/test_resources/ssl/CA_db/cert8.db b/qpid/java/test-profiles/test_resources/ssl/CA_db/cert8.db Binary files differnew file mode 100644 index 0000000000..846e59e82d --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/CA_db/cert8.db diff --git a/qpid/java/test-profiles/test_resources/ssl/CA_db/key3.db b/qpid/java/test-profiles/test_resources/ssl/CA_db/key3.db Binary files differnew file mode 100644 index 0000000000..dd60e1e05e --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/CA_db/key3.db diff --git a/qpid/java/test-profiles/test_resources/ssl/CA_db/rootca.crt b/qpid/java/test-profiles/test_resources/ssl/CA_db/rootca.crt new file mode 100644 index 0000000000..d9cdd9891c --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/CA_db/rootca.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICDDCCAXWgAwIBAgIFAJBNP3QwDQYJKoZIhvcNAQEFBQAwQTELMAkGA1UEBhMC
+Q0ExEDAOBgNVBAgTB09udGFyaW8xDTALBgNVBAoTBEFDTUUxETAPBgNVBAMTCE15
+Um9vdENBMB4XDTEwMDMyMjIxMDAyMloXDTE1MDMyMjIxMDAyMlowQTELMAkGA1UE
+BhMCQ0ExEDAOBgNVBAgTB09udGFyaW8xDTALBgNVBAoTBEFDTUUxETAPBgNVBAMT
+CE15Um9vdENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDICe+SkXU9NRCk
+s+Tmai/j+3uDfJ4mVEt4PRkRWTVVHvuMvbPfKhdBRYRM5wmQmSCOi25Xd9jnh3PF
+BwE+pfaSgVqQiilUYqYak56ZR1Ll0nGwyXZQnW3lTf9VboEl0p67qckcd8SmaJf2
+0lAlTu2W7kJ8whYYyYRqaw+3yA6dGQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4GBADUbCNoxvFbPv+vqfQJ59p8P0cArEPajHR51omE1BbLc
+TfouOIidiBORf1n8DzE7k2Pf//nUHWhJLBP7J7CMs18UYsDD+0aa9A3BZi4wcsYX
+AW9EiXAIhnCk2+yyZyI1gdOnRS/9aOBRFSa1ngCb9GLm4kFzakiDQ1iX7k9dk17p +-----END CERTIFICATE----- diff --git a/qpid/java/test-profiles/test_resources/ssl/CA_db/secmod.db b/qpid/java/test-profiles/test_resources/ssl/CA_db/secmod.db Binary files differnew file mode 100644 index 0000000000..a13e3e602c --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/CA_db/secmod.db diff --git a/qpid/java/test-profiles/test_resources/ssl/app1.crt b/qpid/java/test-profiles/test_resources/ssl/app1.crt new file mode 100644 index 0000000000..1f67a0d152 --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/app1.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIFAJBNWvMwDQYJKoZIhvcNAQEFBQAwQTELMAkGA1UEBhMC
+Q0ExEDAOBgNVBAgTB09udGFyaW8xDTALBgNVBAoTBEFDTUUxETAPBgNVBAMTCE15
+Um9vdENBMB4XDTEwMDMyMjIyMDE0MloXDTEwMDYyMjIyMDE0MlowbTEPMA0GA1UE
+BhMGQ2FuYWRhMQswCQYDVQQIEwJPTjEQMA4GA1UEBxMHVG9yb250bzERMA8GA1UE
+ChMIYWNtZS5vcmcxEDAOBgNVBAsTB1Vua25vd24xFjAUBgNVBAMMDWFwcDFAYWNt
+ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKPmoysNdOgH/XMircR7
+aQxWKqe3tzwREdHdLUmWqanlyfL7zi9P6P3CyMQQsKpM6FkQQlZ+WpE9IIAqP+g1
+3zbFtqdqhfQwsEovcFOLAdDfm6W5YSPN7dPEgf6+DEtTb/9fxWqdtEiXc+PhRd+9
+Inzo2C33gqDjFcA2LcM37zgPAgMBAAGjIjAgMAkGA1UdEwQCMAAwEwYDVR0lBAww
+CgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAs4IQUMJOlAEG7OVLoazgn8ea
+qOPFZ/Y8CYK0yoVZ+QF3aIFx5plj9Rol9Cf96eL6Ta/ff/fvFzUCGceY1TriHxqv
+9/8IR9LdmOaCC3Er2hRjuPumuwimpNGQqsrONPfBrhHVuQXOJYOWRYwuLEG3bQR5
+tP8zTMkBo3/FrX6nZks= +-----END CERTIFICATE----- diff --git a/qpid/java/test-profiles/test_resources/ssl/app1.req b/qpid/java/test-profiles/test_resources/ssl/app1.req new file mode 100644 index 0000000000..b1889d15e1 --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/app1.req @@ -0,0 +1,10 @@ +-----BEGIN NEW CERTIFICATE REQUEST----- +MIIBrTCCARYCAQAwbTEPMA0GA1UEBhMGQ2FuYWRhMQswCQYDVQQIEwJPTjEQMA4GA1UEBxMHVG9y +b250bzERMA8GA1UEChMIYWNtZS5vcmcxEDAOBgNVBAsTB1Vua25vd24xFjAUBgNVBAMMDWFwcDFA +YWNtZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKPmoysNdOgH/XMircR7aQxWKqe3 +tzwREdHdLUmWqanlyfL7zi9P6P3CyMQQsKpM6FkQQlZ+WpE9IIAqP+g13zbFtqdqhfQwsEovcFOL +AdDfm6W5YSPN7dPEgf6+DEtTb/9fxWqdtEiXc+PhRd+9Inzo2C33gqDjFcA2LcM37zgPAgMBAAGg +ADANBgkqhkiG9w0BAQQFAAOBgQBnPCA6n+5y1azadoDcmttFJP6P+jfqp069UIi8zhuIVccJXYkL +pmp9kWolbvj5niVyFTcMnlH7uKMCoUP8sTdCfoSFr3BgkfCV8Wb/P2vl5J/BVCwSt0Uhsue95aAn +8A0tCdyTRWRnLeCmFJ/OiG6vbsBtbjXTxQIJsLr6hLcMKg== +-----END NEW CERTIFICATE REQUEST----- diff --git a/qpid/java/test-profiles/test_resources/ssl/app2.crt b/qpid/java/test-profiles/test_resources/ssl/app2.crt new file mode 100644 index 0000000000..ec4d40f0ce --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/app2.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICRzCCAbCgAwIBAgIFAJBNWx4wDQYJKoZIhvcNAQEFBQAwQTELMAkGA1UEBhMC
+Q0ExEDAOBgNVBAgTB09udGFyaW8xDTALBgNVBAoTBEFDTUUxETAPBgNVBAMTCE15
+Um9vdENBMB4XDTEwMDMyMjIyMDIwNFoXDTEwMDYyMjIyMDIwNFowajELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAk1BMREwDwYDVQQHEwhXZXN0Zm9yZDERMA8GA1UEChMI
+YWNtZS5vcmcxEDAOBgNVBAsTB1Vua25vd24xFjAUBgNVBAMMDWFwcDJAYWNtZS5v
+cmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ9hkHkiTpwQ0cViMyfs9PgU
+5wZRTo2OGHxmw+EBlOvLBPamZ0zF2Rnqc/BRTCkoGQIF0nTFFyALsCxnmyZyje00
+ht0zOtc91aQl5W/c1ShEt7YlcSkDNDVxzouG1Nf7VZSbVbRtUkhhRRqAw84IV0GA
+1kRmZ8oEkvdeBULOYncrAgMBAAGjIjAgMAkGA1UdEwQCMAAwEwYDVR0lBAwwCgYI
+KwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAWFNqhydLqg6SXo9IrOwSxAw3zNoz
+sX4ARdMP1LdqLmpiZ1Qr5C54o19bR7UJfyzsYMuXDg6wy/l0JTcs62qDwD6IdEg1
+ZPkAfoVw4eiCorjM02fFTAvPX6jyYJ/3oQI1POoxhdqhll70WSSaKoSooJFcOG74
+xpacadqA8A0ICAc= +-----END CERTIFICATE----- diff --git a/qpid/java/test-profiles/test_resources/ssl/app2.req b/qpid/java/test-profiles/test_resources/ssl/app2.req new file mode 100644 index 0000000000..7e5282d8b5 --- /dev/null +++ b/qpid/java/test-profiles/test_resources/ssl/app2.req @@ -0,0 +1,10 @@ +-----BEGIN NEW CERTIFICATE REQUEST----- +MIIBqjCCARMCAQAwajELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1BMREwDwYDVQQHEwhXZXN0Zm9y +ZDERMA8GA1UEChMIYWNtZS5vcmcxEDAOBgNVBAsTB1Vua25vd24xFjAUBgNVBAMMDWFwcDJAYWNt +ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ9hkHkiTpwQ0cViMyfs9PgU5wZRTo2O +GHxmw+EBlOvLBPamZ0zF2Rnqc/BRTCkoGQIF0nTFFyALsCxnmyZyje00ht0zOtc91aQl5W/c1ShE +t7YlcSkDNDVxzouG1Nf7VZSbVbRtUkhhRRqAw84IV0GA1kRmZ8oEkvdeBULOYncrAgMBAAGgADAN +BgkqhkiG9w0BAQQFAAOBgQBmlkaYAYn7h3oN54DljF6OJYqBN3RSd4KBa+oEs+2sEyh7kF6PqQpl +cVpBOQx+sfUOUzGWML3Jzi4ub6uMmj3ghWGhyiV1hV2/HtYpkLVRedwbidHUUhROQO01ZODZsteZ +LSMV4KRIENVswp3IsbhghvHTB+BlQV/JufR/m9ORjA== +-----END NEW CERTIFICATE REQUEST----- diff --git a/qpid/java/test-profiles/test_resources/ssl/certstore.jks b/qpid/java/test-profiles/test_resources/ssl/certstore.jks Binary files differindex 57460491fe..d427808eb1 100644 --- a/qpid/java/test-profiles/test_resources/ssl/certstore.jks +++ b/qpid/java/test-profiles/test_resources/ssl/certstore.jks diff --git a/qpid/java/test-profiles/test_resources/ssl/keystore.jks b/qpid/java/test-profiles/test_resources/ssl/keystore.jks Binary files differindex 8e033ec932..fd44841028 100644 --- a/qpid/java/test-profiles/test_resources/ssl/keystore.jks +++ b/qpid/java/test-profiles/test_resources/ssl/keystore.jks diff --git a/qpid/java/test-profiles/test_resources/ssl/server_db/cert8.db b/qpid/java/test-profiles/test_resources/ssl/server_db/cert8.db Binary files differindex 3063a1fef3..d55f529c52 100644 --- a/qpid/java/test-profiles/test_resources/ssl/server_db/cert8.db +++ b/qpid/java/test-profiles/test_resources/ssl/server_db/cert8.db diff --git a/qpid/java/test-profiles/test_resources/ssl/server_db/key3.db b/qpid/java/test-profiles/test_resources/ssl/server_db/key3.db Binary files differindex be86b4af4b..12f3c394c5 100644 --- a/qpid/java/test-profiles/test_resources/ssl/server_db/key3.db +++ b/qpid/java/test-profiles/test_resources/ssl/server_db/key3.db diff --git a/qpid/java/test-profiles/test_resources/ssl/server_db/secmod.db b/qpid/java/test-profiles/test_resources/ssl/server_db/secmod.db Binary files differindex 9c71db0abe..97a5b9b2fe 100644 --- a/qpid/java/test-profiles/test_resources/ssl/server_db/secmod.db +++ b/qpid/java/test-profiles/test_resources/ssl/server_db/secmod.db diff --git a/qpid/java/test-profiles/test_resources/ssl/server_db/server.crt b/qpid/java/test-profiles/test_resources/ssl/server_db/server.crt index eb9323ff34..4f1c007d16 100644 --- a/qpid/java/test-profiles/test_resources/ssl/server_db/server.crt +++ b/qpid/java/test-profiles/test_resources/ssl/server_db/server.crt @@ -1,12 +1,14 @@ -----BEGIN CERTIFICATE----- -MIIBuDCCASGgAwIBAgIFAIzxXHYwDQYJKoZIhvcNAQEEBQAwETEPMA0GA1UEAxMG
-Um9vdENBMB4XDTA5MDQxNDIxNTUyOVoXDTEyMDQxNDIxNTUyOVowIDEeMBwGA1UE
-AxMVbG9jYWxob3N0LmxvY2FsZG9tYWluMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
-iQKBgQDNyXKaIcdDsBrcfsTRhIIsCGPCPKRuzN4w24PjfL72G7v0eyvKuposWDLf
-Os9T5ijaimYkbCyR+evnxFII/lOBFXGtzorTUnfVPvdIr8CEqjdTjJlCjT/rxjd0
-08kiMC9V4ohefnglA3UMBxm1st3IP6JzlUXlZqZdrfq1LLnLqQIDAQABow0wCzAJ
-BgNVHRMEAjAAMA0GCSqGSIb3DQEBBAUAA4GBAKkbAt9ockhmcfLGpyILfTUTqVqU
-Ys2VrOSDaJIxuQEouWNx9bIngKyBV23AvDbQ2Nb9QI8cuzu7laydO//obPrLpvH1
-MbOyd3j+JNNml9mDZw2rR8QpOvC9YDzBVcZgmw8QnHbTHYYdjUIGbXtWvG93gWTj
-QYVlvktPF1aM3RrM +MIICKzCCAZSgAwIBAgIFAJBNUhEwDQYJKoZIhvcNAQEFBQAwQTELMAkGA1UEBhMC
+Q0ExEDAOBgNVBAgTB09udGFyaW8xDTALBgNVBAoTBEFDTUUxETAPBgNVBAMTCE15
+Um9vdENBMB4XDTEwMDMyMjIxNDE0OVoXDTE1MDMyMjIxNDE0OVowTjELMAkGA1UE
+BhMCQ0ExEDAOBgNVBAgTB09udGFyaW8xDTALBgNVBAoTBEFDTUUxHjAcBgNVBAMT
+FWxvY2FsaG9zdC5sb2NhbGRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAtmFy+0IIn6otWu2TCJ3fN2UDA//EVDWpiozzvd/My31XpPQ8jhuvsZ2//xvG
+OKqDfgQ80OE6BiGmR2zxPKFfsgxhU+0g8132focOzd0MhmGpyhdQdogXQ2cCcvjB
+CvgaugIjTmk3MX9njD7np8TQQ7wW1Wuk/c99tuvlexjEoWkCAwEAAaMiMCAwCQYD
+VR0TBAIwADATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOBgQDH
+iOxUWKllSUgCcAij0Gb4qDo/YDoatWyzE8pNZR7OgSOJ8zEVJcB/7YW//frd2dMS
+lz6c38vqbGwyblw+b64SvBKI3WK0jyO4Ft9FGpNIEfc9Q5G0MFiGilv+GDIZ/asE
+KnsZNy4z4gs3KFSf96k1AV1YQ1tVpFcUrI+3QqdWfQ== -----END CERTIFICATE----- diff --git a/qpid/java/test-profiles/test_resources/ssl/server_db/server.req b/qpid/java/test-profiles/test_resources/ssl/server_db/server.req index a5a3fb2e35..5551516586 100644 --- a/qpid/java/test-profiles/test_resources/ssl/server_db/server.req +++ b/qpid/java/test-profiles/test_resources/ssl/server_db/server.req @@ -4,17 +4,18 @@ Phone: (not specified) Common Name: localhost.localdomain Email: (not specified) -Organization: (not specified) -State: (not specified) -Country: (not specified) +Organization: ACME +State: Ontario +Country: CA -----BEGIN NEW CERTIFICATE REQUEST----- -MIIBXzCByQIBADAgMR4wHAYDVQQDExVsb2NhbGhvc3QubG9jYWxkb21haW4wgZ8w
-DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM3Jcpohx0OwGtx+xNGEgiwIY8I8pG7M
-3jDbg+N8vvYbu/R7K8q6mixYMt86z1PmKNqKZiRsLJH56+fEUgj+U4EVca3OitNS
-d9U+90ivwISqN1OMmUKNP+vGN3TTySIwL1XiiF5+eCUDdQwHGbWy3cg/onOVReVm
-pl2t+rUsucupAgMBAAGgADANBgkqhkiG9w0BAQQFAAOBgQCD9+h4+q7Snw4F5E4i
-oCu9SvUgTpMs6ClZUoaCJzjVmkygZwyq38iZV0W6I94MXZ9PFbvyiZkKy0t2oMNk
-J33NOmaHoKOylYBkVlhHjknyYbvcL0Uwoj0/fyRbSZdllhAHUJgrjMBwPKks8+UJ
-0crBkyRYg2gSCLQaPwJPm4ddpw== +MIIBjTCB9wIBADBOMQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzENMAsG
+A1UEChMEQUNNRTEeMBwGA1UEAxMVbG9jYWxob3N0LmxvY2FsZG9tYWluMIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2YXL7Qgifqi1a7ZMInd83ZQMD/8RUNamK
+jPO938zLfVek9DyOG6+xnb//G8Y4qoN+BDzQ4ToGIaZHbPE8oV+yDGFT7SDzXfZ+
+hw7N3QyGYanKF1B2iBdDZwJy+MEK+Bq6AiNOaTcxf2eMPuenxNBDvBbVa6T9z322
+6+V7GMShaQIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAJGqdJVTScR4rzusrf6dE
+Snz/PtKcl8ZqXfHMPhj5uqUACcj3AxvlerIrpIGG9YT5cX4cOa92plpearMAC1TW
+fksbpsJR174WnAbBETrNbOX55igS/KDkW+RJCn7GGOvcSza+nJ880/lZ0iC63bgY
+SmvSnp3ub1CBX0grWl4bzTw= -----END NEW CERTIFICATE REQUEST----- diff --git a/qpid/python/examples/pubsub/verify.in b/qpid/python/examples/pubsub/verify.in index 1b74acd832..ac1506b324 100644 --- a/qpid/python/examples/pubsub/verify.in +++ b/qpid/python/examples/pubsub/verify.in @@ -1,5 +1,18 @@ ==== topic_publisher.py.out ==== topic_subscriber.py.out | remove_uuid | sort +Messages on 'europe' queue: +Messages on 'news' queue: +Messages on 'usa' queue: +Messages on 'weather' queue: +Queues created - please start the topic producer +Subscribing local queue 'local_europe' to europe-' +Subscribing local queue 'local_news' to news-' +Subscribing local queue 'local_usa' to usa-' +Subscribing local queue 'local_weather' to weather-' +That's all, folks! +That's all, folks! +That's all, folks! +That's all, folks! europe.news 0 europe.news 0 europe.news 1 @@ -20,19 +33,6 @@ europe.weather 3 europe.weather 3 europe.weather 4 europe.weather 4 -Messages on 'europe' queue: -Messages on 'news' queue: -Messages on 'usa' queue: -Messages on 'weather' queue: -Queues created - please start the topic producer -Subscribing local queue 'local_europe' to europe-' -Subscribing local queue 'local_news' to news-' -Subscribing local queue 'local_usa' to usa-' -Subscribing local queue 'local_weather' to weather-' -That's all, folks! -That's all, folks! -That's all, folks! -That's all, folks! usa.news 0 usa.news 0 usa.news 1 diff --git a/qpid/python/qpid/brokertest.py b/qpid/python/qpid/brokertest.py index 4feb7413d1..192228a74a 100644 --- a/qpid/python/qpid/brokertest.py +++ b/qpid/python/qpid/brokertest.py @@ -38,11 +38,10 @@ EXPECT_EXIT_FAIL=2 # Expect to exit with non-0 status before end of test EXPECT_RUNNING=3 # Expect to still be running at end of test EXPECT_UNKNOWN=4 # No expectation, don't check exit status. -def is_exe(fpath): - return os.path.exists(fpath) and os.access(fpath, os.X_OK) - def find_exe(program): """Find an executable in the system PATH""" + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) dir, name = os.path.split(program) if dir: if is_exe(program): return program @@ -144,13 +143,13 @@ class Popen(popen2.Popen3): expect - if set verify expectation at end of test. drain - if true (default) drain stdout/stderr to files. """ - assert find_exe(cmd[0]) + 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 ] popen2.Popen3.__init__(self, self.cmd, True) self.expect = expect self.was_shutdown = False # Set if we deliberately kill/terminate the process - self.pname = "%s-%d" % (os.path.split(self.cmd[0])[-1], self.pid) + self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.pid) msg = "Process %s" % self.pname self.stdin = ExceptionWrapper(self.tochild, msg) self.stdout = Popen.OutStream(self.fromchild, self.outfile("out"), msg) @@ -179,6 +178,7 @@ class Popen(popen2.Popen3): def stop(self): # Clean up at end of test. self.drain() + self.stdin.close() if self.expect == EXPECT_UNKNOWN: try: self.kill() # Just make sure its dead except: pass @@ -267,8 +267,9 @@ class Broker(Popen): cmd += ["--data-dir", self.datadir] Popen.__init__(self, cmd, expect, drain=False) test.cleanup_stop(self) - self._host = "localhost" + self._host = "127.0.0.1" log.debug("Started broker %s (%s, %s)" % (self.name, self.pname, self.log)) + self._log_ready = False def host(self): return self._host @@ -302,8 +303,8 @@ class Broker(Popen): c.close() def _prep_sender(self, queue, durable, xprops): - s = queue + "; {create:always, node-properties:{durable:" + str(durable) - if xprops != None: s += ", x-properties:{" + xprops + "}" + s = queue + "; {create:always, node:{durable:" + str(durable) + if xprops != None: s += ", x-declare:{" + xprops + "}" return s + "}}" def send_message(self, queue, message, durable=True, xprops=None, session=None): @@ -344,16 +345,17 @@ 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 if not os.path.exists(self.log): return False - ready_msg = re.compile("notice Broker running") f = file(self.log) try: for l in f: - if ready_msg.search(l): return True + if "notice Broker running" in l: + self._log_ready = True + return True return False finally: f.close() - # FIXME aconway 2010-03-02: rename to wait_ready def ready(self): """Wait till broker is ready to serve clients""" # First make sure the broker is listening by checking the log. @@ -361,7 +363,7 @@ class Broker(Popen): raise Exception("Timed out waiting for broker %s" % self.name) # Make a connection, this will wait for extended cluster init to finish. try: self.connect().close() - except: raise RethrownException("Broker %s failed ready test %s"%self.name) + except: raise RethrownException("Broker %s failed ready test"%self.name) class Cluster: """A cluster of brokers in a test.""" @@ -427,6 +429,7 @@ class BrokerTest(TestCase): for p in self.stopem: try: p.stop() except Exception, e: err.append(str(e)) + if err: raise Exception("Unexpected process status:\n "+"\n ".join(err)) def cleanup_stop(self, stopable): @@ -446,7 +449,7 @@ class BrokerTest(TestCase): if (wait): try: b.ready() except Exception, e: - raise Exception("Failed to start broker %s: %s" % ( b.name, e)) + raise Exception("Failed to start broker %s(%s): %s" % (b.name, b.log, e)) return b def cluster(self, count=0, args=[], expect=EXPECT_RUNNING, wait=True): diff --git a/qpid/python/qpid/messaging/constants.py b/qpid/python/qpid/messaging/constants.py index cad47bd52a..f230c4def8 100644 --- a/qpid/python/qpid/messaging/constants.py +++ b/qpid/python/qpid/messaging/constants.py @@ -17,11 +17,16 @@ # under the License. # +__SELF__ = object() + class Constant: - def __init__(self, name, value=None): + def __init__(self, name, value=__SELF__): self.name = name - self.value = value + if value is __SELF__: + self.value = self + else: + self.value = value def __repr__(self): return self.name @@ -30,3 +35,6 @@ AMQP_PORT = 5672 AMQPS_PORT = 5671 UNLIMITED = Constant("UNLIMITED", 0xFFFFFFFFL) + +REJECTED = Constant("REJECTED") +RELEASED = Constant("RELEASED") diff --git a/qpid/python/qpid/messaging/driver.py b/qpid/python/qpid/messaging/driver.py index d0f5b746f3..ba53d94e33 100644 --- a/qpid/python/qpid/messaging/driver.py +++ b/qpid/python/qpid/messaging/driver.py @@ -18,7 +18,7 @@ # import socket, struct, sys, time -from logging import getLogger +from logging import getLogger, DEBUG from qpid import compat from qpid import sasl from qpid.concurrency import synchronized @@ -27,13 +27,13 @@ from qpid.exceptions import Timeout, VersionError from qpid.framing import OpEncoder, SegmentEncoder, FrameEncoder, \ FrameDecoder, SegmentDecoder, OpDecoder from qpid.messaging import address -from qpid.messaging.constants import UNLIMITED +from qpid.messaging.constants import UNLIMITED, REJECTED, RELEASED from qpid.messaging.exceptions import ConnectError -from qpid.messaging.message import get_codec, Message +from qpid.messaging.message import get_codec, Disposition, Message from qpid.ops import * from qpid.selector import Selector from qpid.util import connect -from qpid.validator import And, Context, Map, Types, Values +from qpid.validator import And, Context, List, Map, Types, Values from threading import Condition, Thread log = getLogger("qpid.messaging") @@ -78,9 +78,8 @@ class Pattern: sst.write_cmd(ExchangeBind(exchange=exchange, queue=queue, binding_key=self.value.replace("*", "#"))) -FILTER_DEFAULTS = { - "topic": Pattern("*"), - "amq.failover": Pattern("DUMMY") +SUBJECT_DEFAULTS = { + "topic": "#" } # XXX @@ -130,7 +129,14 @@ class SessionState: id = self.sent self.write_cmd(query, lambda: handler(self.results.pop(id))) - def write_cmd(self, cmd, action=noop): + def apply_overrides(self, cmd, overrides): + for k, v in overrides.items(): + cmd[k.replace('-', '_')] = v + + def write_cmd(self, cmd, action=noop, overrides=None): + if overrides: + self.apply_overrides(cmd, overrides) + if action != noop: cmd.sync = True if self.detached: @@ -154,28 +160,36 @@ class SessionState: self.driver.write_op(op) POLICIES = Values("always", "sender", "receiver", "never") +RELIABILITY = Values("unreliable", "at-most-once", "at-least-once", + "exactly-once") -class Bindings: - - def validate(self, o, ctx): - t = ctx.containers[1].get("type", "queue") - if t != "queue": - return "bindings are only permitted on nodes of type queue" +DECLARE = Map({}, restricted=False) +BINDINGS = List(Map({ + "exchange": Types(basestring), + "queue": Types(basestring), + "key": Types(basestring), + "arguments": Map({}, restricted=False) + })) COMMON_OPTS = { - "create": POLICIES, - "delete": POLICIES, - "assert": POLICIES, - "node-properties": Map({ - "type": Values("queue", "topic"), - "durable": Types(bool), - "x-properties": Map({ - "type": Types(basestring), - "bindings": And(Types(list), Bindings()) - }, - restricted=False) - }) - } + "create": POLICIES, + "delete": POLICIES, + "assert": POLICIES, + "node": Map({ + "type": Values("queue", "topic"), + "durable": Types(bool), + "x-declare": DECLARE, + "x-bindings": BINDINGS + }), + "link": Map({ + "name": Types(basestring), + "durable": Types(bool), + "reliability": RELIABILITY, + "x-declare": DECLARE, + "x-bindings": BINDINGS, + "x-subscribe": Map({}, restricted=False) + }) + } RECEIVE_MODES = Values("browse", "consume") @@ -196,36 +210,46 @@ class LinkIn: _rcv.destination = str(rcv.id) sst.destinations[_rcv.destination] = _rcv _rcv.draining = False + _rcv.on_unlink = [] def do_link(self, sst, rcv, _rcv, type, subtype, action): + link_opts = _rcv.options.get("link", {}) + # XXX: default? + reliability = link_opts.get("reliability", "unreliable") + declare = link_opts.get("x-declare", {}) + subscribe = link_opts.get("x-subscribe", {}) acq_mode = acquire_mode.pre_acquired if type == "topic": - _rcv._queue = "%s.%s" % (rcv.session.name, _rcv.destination) - sst.write_cmd(QueueDeclare(queue=_rcv._queue, durable=DURABLE_DEFAULT, exclusive=True, auto_delete=True)) - filter = _rcv.options.get("filter") - if _rcv.subject is None and filter is None: - f = FILTER_DEFAULTS[subtype] - elif _rcv.subject and filter: - # XXX - raise Exception("can't supply both subject and filter") - elif _rcv.subject: - # XXX - f = Pattern(_rcv.subject) - else: - f = filter - f._bind(sst, _rcv.name, _rcv._queue) + default_name = "%s.%s" % (rcv.session.name, _rcv.destination) + _rcv._queue = link_opts.get("name", default_name) + sst.write_cmd(QueueDeclare(queue=_rcv._queue, + durable=link_opts.get("durable", False), + exclusive=True, + auto_delete=(reliability == "unreliable")), + overrides=declare) + _rcv.on_unlink = [QueueDelete(_rcv._queue)] + subject = _rcv.subject or SUBJECT_DEFAULTS.get(subtype) + sst.write_cmd(ExchangeBind(_rcv._queue, _rcv.name, subject)) + bindings = get_bindings(link_opts, _rcv._queue, _rcv.name, subject) elif type == "queue": _rcv._queue = _rcv.name if _rcv.options.get("mode", "consume") == "browse": acq_mode = acquire_mode.not_acquired + bindings = get_bindings(link_opts, queue=_rcv._queue) + sst.write_cmds(bindings) sst.write_cmd(MessageSubscribe(queue=_rcv._queue, destination=_rcv.destination, - acquire_mode = acq_mode)) + acquire_mode = acq_mode), + overrides=subscribe) sst.write_cmd(MessageSetFlowMode(_rcv.destination, flow_mode.credit), action) def do_unlink(self, sst, rcv, _rcv, action=noop): - sst.write_cmd(MessageCancel(_rcv.destination), action) + link_opts = _rcv.options.get("link", {}) + reliability = link_opts.get("reliability") + cmds = [MessageCancel(_rcv.destination)] + cmds.extend(_rcv.on_unlink) + sst.write_cmds(cmds, action) def del_link(self, sst, rcv, _rcv): del sst.destinations[_rcv.destination] @@ -240,13 +264,16 @@ class LinkOut: _snd.closing = False def do_link(self, sst, snd, _snd, type, subtype, action): + link_opts = _snd.options.get("link", {}) if type == "topic": _snd._exchange = _snd.name _snd._routing_key = _snd.subject + bindings = get_bindings(link_opts, exchange=_snd.name, key=_snd.subject) elif type == "queue": _snd._exchange = "" _snd._routing_key = _snd.name - action() + bindings = get_bindings(link_opts, queue=_snd.name) + sst.write_cmds(bindings, action) def do_unlink(self, sst, snd, _snd, action=noop): action() @@ -435,6 +462,19 @@ class Driver: self._host = (self._host + 1) % len(self._hosts) self.close_engine(e) +DEFAULT_DISPOSITION = Disposition(None) + +def get_bindings(opts, queue=None, exchange=None, key=None): + bindings = opts.get("x-bindings", []) + cmds = [] + for b in bindings: + exchange = b.get("exchange", exchange) + queue = b.get("queue", queue) + key = b.get("key", key) + args = b.get("arguments", {}) + cmds.append(ExchangeBind(queue, exchange, key, args)) + return cmds + class Engine: def __init__(self, connection): @@ -783,12 +823,6 @@ class Engine: err = self.declare(sst, lnk, action) else: err = ("no such queue: %s" % lnk.name,) - elif type == "queue": - try: - cmds = self.bindings(lnk) - sst.write_cmds(cmds, lambda: action(type, subtype)) - except address.ParseError, e: - err = (e,) else: action(type, subtype) @@ -829,23 +863,21 @@ class Engine: def declare(self, sst, lnk, action): name = lnk.name - props = lnk.options.get("node-properties", {}) + props = lnk.options.get("node", {}) durable = props.get("durable", DURABLE_DEFAULT) type = props.get("type", "queue") - xprops = props.get("x-properties", {}) + declare = props.get("x-declare", {}) if type == "topic": cmd = ExchangeDeclare(exchange=name, durable=durable) + bindings = get_bindings(props, exchange=name) elif type == "queue": cmd = QueueDeclare(queue=name, durable=durable) + bindings = get_bindings(props, queue=name) else: raise ValueError(type) - for f in cmd.FIELDS: - if f.name != "arguments" and xprops.has_key(f.name): - cmd[f.name] = xprops.pop(f.name) - if xprops: - cmd.arguments = xprops + sst.apply_overrides(cmd, declare) if type == "topic": if cmd.type is None: @@ -855,11 +887,7 @@ class Engine: subtype = None cmds = [cmd] - if type == "queue": - try: - cmds.extend(self.bindings(lnk)) - except address.ParseError, e: - return (e,) + cmds.extend(bindings) def declared(): self.address_cache[name] = (type, subtype) @@ -867,16 +895,6 @@ class Engine: sst.write_cmds(cmds, declared) - def bindings(self, lnk): - props = lnk.options.get("node-properties", {}) - xprops = props.get("x-properties", {}) - bindings = xprops.get("bindings", []) - cmds = [] - for b in bindings: - n, s, o = address.parse(b) - cmds.append(ExchangeBind(lnk.name, n, s, o)) - return cmds - def delete(self, sst, name, action): def deleted(): del self.address_cache[name] @@ -915,19 +933,49 @@ class Engine: if ssn.acked: messages = [m for m in ssn.acked if m not in sst.acked] if messages: - # XXX: we're ignoring acks that get lost when disconnected, - # could we deal this via some message-id based purge? - ids = RangedSet(*[m._transfer_id for m in messages if m._transfer_id is not None]) + ids = RangedSet() + + disposed = [(DEFAULT_DISPOSITION, [])] + for m in messages: + # XXX: we're ignoring acks that get lost when disconnected, + # could we deal this via some message-id based purge? + if m._transfer_id is None: + continue + ids.add(m._transfer_id) + disp = m._disposition or DEFAULT_DISPOSITION + last, msgs = disposed[-1] + if disp.type is last.type and disp.options == last.options: + msgs.append(m) + else: + disposed.append((disp, [m])) + for range in ids: sst.executed.add_range(range) sst.write_op(SessionCompleted(sst.executed)) - def ack_ack(): - for m in messages: - ssn.acked.remove(m) - if not ssn.transactional: - sst.acked.remove(m) - sst.write_cmd(MessageAccept(ids), ack_ack) - log.debug("SACK[%s]: %s", ssn.log_id, m) + + def ack_acker(msgs): + def ack_ack(): + for m in msgs: + ssn.acked.remove(m) + if not ssn.transactional: + sst.acked.remove(m) + return ack_ack + + for disp, msgs in disposed: + if not msgs: continue + if disp.type is None: + op = MessageAccept + elif disp.type is RELEASED: + op = MessageRelease + elif disp.type is REJECTED: + op = MessageReject + sst.write_cmd(op(RangedSet(*[m._transfer_id for m in msgs]), + **disp.options), + ack_acker(msgs)) + if log.isEnabledFor(DEBUG): + for m in msgs: + log.debug("SACK[%s]: %s, %s", ssn.log_id, m, m._disposition) + sst.acked.extend(messages) if ssn.committing and not sst.committing: @@ -948,7 +996,7 @@ class Engine: for range in ids: sst.executed.add_range(range) sst.write_op(SessionCompleted(sst.executed)) - sst.write_cmd(MessageRelease(ids)) + sst.write_cmd(MessageRelease(ids, True)) sst.write_cmd(TxRollback(), do_rb_ok) def do_rb_ok(): @@ -1055,8 +1103,11 @@ class Engine: if mp.application_headers is None: mp.application_headers = {} mp.application_headers[TO] = msg.to - if msg.durable: - dp.delivery_mode = delivery_mode.persistent + if msg.durable is not None: + if msg.durable: + dp.delivery_mode = delivery_mode.persistent + else: + dp.delivery_mode = delivery_mode.non_persistent if msg.priority is not None: dp.priority = msg.priority if msg.ttl is not None: @@ -1109,7 +1160,8 @@ class Engine: if mp.reply_to is not None: msg.reply_to = reply_to2addr(mp.reply_to) msg.correlation_id = mp.correlation_id - msg.durable = dp.delivery_mode == delivery_mode.persistent + if dp.delivery_mode is not None: + msg.durable = dp.delivery_mode == delivery_mode.persistent msg.priority = dp.priority msg.ttl = dp.ttl msg.redelivered = dp.redelivered diff --git a/qpid/python/qpid/messaging/endpoints.py b/qpid/python/qpid/messaging/endpoints.py index 004cee5f88..af2b1a8007 100644 --- a/qpid/python/qpid/messaging/endpoints.py +++ b/qpid/python/qpid/messaging/endpoints.py @@ -295,14 +295,29 @@ class Session: create: <create-policy>, delete: <delete-policy>, assert: <assert-policy>, - node-properties: { + node: { type: <node-type>, durable: <node-durability>, - x-properties: { - bindings: ["<exchange>/<key>", ...], - <passthrough-key>: <passthrough-value> - } + x-declare: { ... <queue-declare overrides> ... } + x-bindings: [<binding_1>, ... <binding_n>] } + link: { + name: <link-name>, + durable: <link-durability>, + reliability: <link-reliability>, + x-declare: { ... <queue-declare overrides> ... } + x-bindings: [<binding_1>, ... <binding_n>] + x-subscribe: { ... <message-subscribe overrides> ... } + } + } + + Bindings are specified as a map with the following options:: + + { + exchange: <exchange>, + queue: <queue>, + key: <key>, + arguments: <arguments> } The create, delete, and assert policies specify who should perfom @@ -316,14 +331,12 @@ class Session: The node-type is one of: - I{topic}: a topic node will default to the topic exchange, - x-properties may be used to specify other exchange types + x-declare may be used to specify other exchange types - I{queue}: this is the default node-type - The x-properties map permits arbitrary additional keys and values to - be specified. These keys and values are passed through when creating - a node or asserting facts about an existing node. Any passthrough - keys and values that do not match a standard field of the underlying - exchange or queue declare command will be sent in the arguments map. + The x-declare map permits protocol specific keys and values to be + specified. These keys and values are passed through when creating a + node or asserting facts about an existing node. Examples -------- @@ -353,18 +366,18 @@ class Session: You can customize the properties of the queue:: - my-queue; {create: always, node-properties: {durable: True}} + my-queue; {create: always, node: {durable: True}} You can create a topic instead if you want:: - my-queue; {create: always, node-properties: {type: topic}} + my-queue; {create: always, node: {type: topic}} You can assert that the address resolves to a node with particular properties:: my-transient-topic; { assert: always, - node-properties: { + node: { type: topic, durable: False } @@ -508,7 +521,7 @@ class Session: raise Empty @synchronized - def acknowledge(self, message=None, sync=True): + def acknowledge(self, message=None, disposition=None, sync=True): """ Acknowledge the given L{Message}. If message is None, then all unacknowledged messages on the session are acknowledged. @@ -530,6 +543,7 @@ class Session: raise InsufficientCapacity("ack_capacity = %s" % self.ack_capacity) self._wakeup() self._ewait(lambda: len(self.acked) < self.ack_capacity) + m._disposition = disposition self.unacked.remove(m) self.acked.append(m) diff --git a/qpid/python/qpid/messaging/message.py b/qpid/python/qpid/messaging/message.py index 46494e428e..a9660b05b1 100644 --- a/qpid/python/qpid/messaging/message.py +++ b/qpid/python/qpid/messaging/message.py @@ -129,7 +129,7 @@ class Message: "correlation_id", "priority", "ttl"]: value = self.__dict__[name] if value is not None: args.append("%s=%r" % (name, value)) - for name in ["durable", "properties"]: + for name in ["durable", "redelivered", "properties"]: value = self.__dict__[name] if value: args.append("%s=%r" % (name, value)) if self.content_type != get_type(self.content): @@ -141,4 +141,15 @@ class Message: args.append(repr(self.content)) return "Message(%s)" % ", ".join(args) -__all__ = ["Message"] +class Disposition: + + def __init__(self, type, **options): + self.type = type + self.options = options + + def __repr__(self): + args = [str(self.type)] + \ + ["%s=%r" % (k, v) for k, v in self.options.items()] + return "Disposition(%s)" % ", ".join(args) + +__all__ = ["Message", "Disposition"] diff --git a/qpid/python/qpid/tests/messaging/__init__.py b/qpid/python/qpid/tests/messaging/__init__.py index eb8ff87391..c3581efb9d 100644 --- a/qpid/python/qpid/tests/messaging/__init__.py +++ b/qpid/python/qpid/tests/messaging/__init__.py @@ -59,6 +59,9 @@ class Base(Test): else: return "%s[%s, %s]" % (base, count, self.test_id) + def message(self, base, count = None, **kwargs): + return Message(content=self.content(base, count), **kwargs) + def ping(self, ssn): PING_Q = 'ping-queue; {create: always, delete: always}' # send a message @@ -70,16 +73,52 @@ class Base(Test): ssn.acknowledge() assert msg.content == content, "expected %r, got %r" % (content, msg.content) - def drain(self, rcv, limit=None, timeout=0, expected=None): - contents = [] + def drain(self, rcv, limit=None, timeout=0, expected=None, redelivered=False): + messages = [] try: - while limit is None or len(contents) < limit: - contents.append(rcv.fetch(timeout=timeout).content) + while limit is None or len(messages) < limit: + messages.append(rcv.fetch(timeout=timeout)) except Empty: pass if expected is not None: - assert expected == contents, "expected %s, got %s" % (expected, contents) - return contents + self.assertEchos(expected, messages, redelivered) + return messages + + def diff(self, m1, m2): + result = {} + for attr in ("id", "subject", "user_id", "to", "reply_to", + "correlation_id", "durable", "priority", "ttl", + "redelivered", "properties", "content_type", + "content"): + a1 = getattr(m1, attr) + a2 = getattr(m2, attr) + if a1 != a2: + result[attr] = (a1, a2) + return result + + def assertEcho(self, msg, echo, redelivered=False): + if not isinstance(msg, Message) or not isinstance(echo, Message): + if isinstance(msg, Message): + msg = msg.content + if isinstance(echo, Message): + echo = echo.content + assert msg == echo, "expected %s, got %s" % (msg, echo) + else: + delta = self.diff(msg, echo) + mttl, ettl = delta.pop("ttl", (0, 0)) + if redelivered: + assert echo.redelivered, \ + "expected %s to be redelivered: %s" % (msg, echo) + if delta.has_key("redelivered"): + del delta["redelivered"] + assert mttl is not None and ettl is not None, "%s, %s" % (mttl, ettl) + assert mttl >= ettl, "%s, %s" % (mttl, ettl) + assert not delta, "expected %s, got %s, delta %s" % (msg, echo, delta) + + def assertEchos(self, msgs, echoes, redelivered=False): + assert len(msgs) == len(echoes), "%s, %s" % (msgs, echoes) + for m, e in zip(msgs, echoes): + self.assertEcho(m, e, redelivered) def assertEmpty(self, rcv): contents = self.drain(rcv) diff --git a/qpid/python/qpid/tests/messaging/endpoints.py b/qpid/python/qpid/tests/messaging/endpoints.py index 6bc52d962d..5d4fc1646b 100644 --- a/qpid/python/qpid/tests/messaging/endpoints.py +++ b/qpid/python/qpid/tests/messaging/endpoints.py @@ -227,21 +227,60 @@ class SessionTests(Base): def testAcknowledgeAsyncAckCapUNLIMITED(self): self.ackTest(lambda ssn: ssn.acknowledge(sync=False), UNLIMITED) - def send(self, ssn, queue, base, count=1): - snd = ssn.sender(queue, durable=self.durable()) - contents = [] + def testRelease(self): + msgs = [self.message("testRelease", i) for i in range(3)] + snd = self.ssn.sender("test-release-queue; {create: always, delete: always}") + for m in msgs: + snd.send(m) + rcv = self.ssn.receiver(snd.target) + echos = self.drain(rcv, expected=msgs) + self.ssn.acknowledge(echos[0]) + self.ssn.acknowledge(echos[1], Disposition(RELEASED, set_redelivered=True)) + self.ssn.acknowledge(echos[2], Disposition(RELEASED)) + self.drain(rcv, limit=1, expected=msgs[1:2], redelivered=True) + self.drain(rcv, expected=msgs[2:3]) + self.ssn.acknowledge() + + def testReject(self): + msgs = [self.message("testReject", i) for i in range(3)] + snd = self.ssn.sender(""" + test-reject-queue; { + create: always, + delete: always, + node: { + x-declare: { + alternate-exchange: 'amq.topic' + } + } + } +""") + for m in msgs: + snd.send(m) + rcv = self.ssn.receiver(snd.target) + rej = self.ssn.receiver("amq.topic") + echos = self.drain(rcv, expected=msgs) + self.ssn.acknowledge(echos[0]) + self.ssn.acknowledge(echos[1], Disposition(REJECTED)) + self.ssn.acknowledge(echos[2], + Disposition(REJECTED, code=3, text="test-reject")) + self.drain(rej, expected=msgs[1:]) + self.ssn.acknowledge() + + def send(self, ssn, target, base, count=1): + snd = ssn.sender(target, durable=self.durable()) + messages = [] for i in range(count): - c = self.content(base, i) + c = self.message(base, i) snd.send(c) - contents.append(c) + messages.append(c) snd.close() - return contents + return messages def txTest(self, commit): TX_Q = 'test-tx-queue; {create: sender, delete: receiver}' TX_Q_COPY = 'test-tx-queue-copy; {create: always, delete: always}' txssn = self.conn.session(transactional=True) - contents = self.send(self.ssn, TX_Q, "txTest", 3) + messages = self.send(self.ssn, TX_Q, "txTest", 3) txrcv = txssn.receiver(TX_Q) txsnd = txssn.sender(TX_Q_COPY, durable=self.durable()) rcv = self.ssn.receiver(txrcv.source) @@ -255,10 +294,10 @@ class SessionTests(Base): if commit: txssn.commit() self.assertEmpty(rcv) - assert contents == self.drain(copy_rcv) + self.drain(copy_rcv, expected=messages) else: txssn.rollback() - assert contents == self.drain(rcv) + self.drain(rcv, expected=messages, redelivered=True) self.assertEmpty(copy_rcv) self.ssn.acknowledge() @@ -271,13 +310,13 @@ class SessionTests(Base): def txTestSend(self, commit): TX_SEND_Q = 'test-tx-send-queue; {create: sender, delete: receiver}' txssn = self.conn.session(transactional=True) - contents = self.send(txssn, TX_SEND_Q, "txTestSend", 3) + messages = self.send(txssn, TX_SEND_Q, "txTestSend", 3) rcv = self.ssn.receiver(TX_SEND_Q) self.assertEmpty(rcv) if commit: txssn.commit() - assert contents == self.drain(rcv) + self.drain(rcv, expected=messages) self.ssn.acknowledge() else: txssn.rollback() @@ -297,18 +336,17 @@ class SessionTests(Base): txssn = self.conn.session(transactional=True) txrcv = txssn.receiver(TX_ACK_QC) self.assertEmpty(txrcv) - contents = self.send(self.ssn, TX_ACK_QC, "txTestAck", 3) - assert contents == self.drain(txrcv) + messages = self.send(self.ssn, TX_ACK_QC, "txTestAck", 3) + self.drain(txrcv, expected=messages) if commit: txssn.acknowledge() else: txssn.rollback() - drained = self.drain(txrcv) - assert contents == drained, "expected %s, got %s" % (contents, drained) + self.drain(txrcv, expected=messages, redelivered=True) txssn.acknowledge() txssn.rollback() - assert contents == self.drain(txrcv) + self.drain(txrcv, expected=messages, redelivered=True) txssn.commit() # commit without ack self.assertEmpty(txrcv) @@ -316,7 +354,7 @@ class SessionTests(Base): txssn = self.conn.session(transactional=True) txrcv = txssn.receiver(TX_ACK_QC) - assert contents == self.drain(txrcv) + self.drain(txrcv, expected=messages, redelivered=True) txssn.acknowledge() txssn.commit() rcv = self.ssn.receiver(TX_ACK_QD) @@ -477,7 +515,7 @@ class ReceiverTests(Base): snd = self.ssn.sender("""test-double-close; { create: always, delete: sender, - node-properties: { + node: { type: topic } } @@ -533,9 +571,9 @@ class AddressTests(Base): assert "error in options: %s" % error == str(e), e def testIllegalKey(self): - self.badOption("{create: always, node-properties: " + self.badOption("{create: always, node: " "{this-property-does-not-exist: 3}}", - "node-properties: this-property-does-not-exist: " + "node: this-property-does-not-exist: " "illegal key") def testWrongValue(self): @@ -543,23 +581,17 @@ class AddressTests(Base): "('always', 'sender', 'receiver', 'never')") def testWrongType1(self): - self.badOption("{node-properties: asdf}", - "node-properties: asdf is not a map") + self.badOption("{node: asdf}", + "node: asdf is not a map") def testWrongType2(self): - self.badOption("{node-properties: {durable: []}}", - "node-properties: durable: [] is not a bool") - - def testNonQueueBindings(self): - self.badOption("{node-properties: {type: topic, x-properties: " - "{bindings: []}}}", - "node-properties: x-properties: bindings: " - "bindings are only permitted on nodes of type queue") + self.badOption("{node: {durable: []}}", + "node: durable: [] is not a bool") def testCreateQueue(self): snd = self.ssn.sender("test-create-queue; {create: always, delete: always, " - "node-properties: {type: queue, durable: False, " - "x-properties: {auto_delete: true}}}") + "node: {type: queue, durable: False, " + "x-declare: {auto_delete: true}}}") content = self.content("testCreateQueue") snd.send(content) rcv = self.ssn.receiver("test-create-queue") @@ -569,10 +601,10 @@ class AddressTests(Base): addr = """test-create-exchange; { create: always, delete: always, - node-properties: { + node: { type: topic, durable: False, - x-properties: {auto_delete: true, %s} + x-declare: {auto_delete: true, %s} } }""" % props snd = self.ssn.sender(addr) @@ -639,15 +671,15 @@ class AddressTests(Base): # XXX: need to figure out close after error self.conn._remove_session(self.ssn) - def testBindings(self): + def testNodeBindingsQueue(self): snd = self.ssn.sender(""" -test-bindings-queue; { +test-node-bindings-queue; { create: always, delete: always, - node-properties: { - x-properties: { - bindings: ["amq.topic/a.#", "amq.direct/b", "amq.topic/c.*"] - } + node: { + x-bindings: [{exchange: "amq.topic", key: "a.#"}, + {exchange: "amq.direct", key: "b"}, + {exchange: "amq.topic", key: "c.*"}] } } """) @@ -658,49 +690,80 @@ test-bindings-queue; { snd_a.send("two") snd_b.send("three") snd_c.send("four") - rcv = self.ssn.receiver("test-bindings-queue") + rcv = self.ssn.receiver("test-node-bindings-queue") self.drain(rcv, expected=["one", "two", "three", "four"]) - def testBindingsAdditive(self): - m1 = self.content("testBindingsAdditive", 1) - m2 = self.content("testBindingsAdditive", 2) - m3 = self.content("testBindingsAdditive", 3) - m4 = self.content("testBindingsAdditive", 4) - + def testNodeBindingsTopic(self): + rcv = self.ssn.receiver("test-node-bindings-topic-queue; {create: always, delete: always}") + rcv_a = self.ssn.receiver("test-node-bindings-topic-queue-a; {create: always, delete: always}") + rcv_b = self.ssn.receiver("test-node-bindings-topic-queue-b; {create: always, delete: always}") + rcv_c = self.ssn.receiver("test-node-bindings-topic-queue-c; {create: always, delete: always}") snd = self.ssn.sender(""" -test-bindings-additive-queue; { +test-node-bindings-topic; { create: always, delete: always, - node-properties: { - x-properties: { - bindings: ["amq.topic/a"] - } + node: { + type: topic, + x-bindings: [{queue: test-node-bindings-topic-queue, key: "#"}, + {queue: test-node-bindings-topic-queue-a, key: "a.#"}, + {queue: test-node-bindings-topic-queue-b, key: "b"}, + {queue: test-node-bindings-topic-queue-c, key: "c.*"}] } } """) + m1 = Message("one") + m2 = Message(subject="a.foo", content="two") + m3 = Message(subject="b", content="three") + m4 = Message(subject="c.bar", content="four") + snd.send(m1) + snd.send(m2) + snd.send(m3) + snd.send(m4) + self.drain(rcv, expected=[m1, m2, m3, m4]) + self.drain(rcv_a, expected=[m2]) + self.drain(rcv_b, expected=[m3]) + self.drain(rcv_c, expected=[m4]) + + def testLinkBindings(self): + m_a = self.message("testLinkBindings", 1, subject="a") + m_b = self.message("testLinkBindings", 2, subject="b") + + self.ssn.sender("test-link-bindings-queue; {create: always, delete: always}") + snd = self.ssn.sender("amq.topic") + + snd.send(m_a) + snd.send(m_b) + snd.close() - snd_a = self.ssn.sender("amq.topic/a") - snd_b = self.ssn.sender("amq.topic/b") + rcv = self.ssn.receiver("test-link-bindings-queue") + self.assertEmpty(rcv) + + snd = self.ssn.sender(""" +amq.topic; { + link: { + x-bindings: [{queue: test-link-bindings-queue, key: a}] + } +} +""") - snd_a.send(m1) - snd_b.send(m2) + snd.send(m_a) + snd.send(m_b) - rcv = self.ssn.receiver("test-bindings-additive-queue") - self.drain(rcv, expected=[m1]) + self.drain(rcv, expected=[m_a]) + rcv.close() - new_snd = self.ssn.sender(""" -test-bindings-additive-queue; { - node-properties: { - x-properties: { - bindings: ["amq.topic/b"] - } + rcv = self.ssn.receiver(""" +test-link-bindings-queue; { + link: { + x-bindings: [{exchange: "amq.topic", key: b}] } } """) - new_snd.send(m3) - snd_b.send(m4) - self.drain(rcv, expected=[m3, m4]) + snd.send(m_a) + snd.send(m_b) + + self.drain(rcv, expected=[m_a, m_b]) def testSubjectOverride(self): snd = self.ssn.sender("amq.topic/a") @@ -726,6 +789,32 @@ test-bindings-additive-queue; { assert e2.subject == "b", "subject: %s" % e2.subject self.assertEmpty(rcv) + def doReliabilityTest(self, reliability, messages, expected): + snd = self.ssn.sender("amq.topic") + rcv = self.ssn.receiver("amq.topic; {link: {reliability: %s}}" % reliability) + for m in messages: + snd.send(m) + self.conn.disconnect() + self.conn.connect() + self.drain(rcv, expected=expected) + + def testReliabilityUnreliable(self): + msgs = [self.message("testReliabilityUnreliable", i) for i in range(3)] + self.doReliabilityTest("unreliable", msgs, []) + + def testReliabilityAtLeastOnce(self): + msgs = [self.message("testReliabilityAtLeastOnce", i) for i in range(3)] + self.doReliabilityTest("at-least-once", msgs, msgs) + + def testLinkName(self): + msgs = [self.message("testLinkName", i) for i in range(3)] + snd = self.ssn.sender("amq.topic") + trcv = self.ssn.receiver("amq.topic; {link: {name: test-link-name}}") + qrcv = self.ssn.receiver("test-link-name") + for m in msgs: + snd.send(m) + self.drain(qrcv, expected=msgs) + NOSUCH_Q = "this-queue-should-not-exist" UNPARSEABLE_ADDR = "name/subject; {bad options" UNLEXABLE_ADDR = "\0x0\0x1\0x2\0x3" @@ -838,8 +927,7 @@ class SenderTests(Base): msgs = [self.content("asyncTest", i) for i in range(15)] for m in msgs: self.snd.send(m, sync=False) - drained = self.drain(self.rcv, timeout=self.delay()) - assert msgs == drained, "expected %s, got %s" % (msgs, drained) + self.drain(self.rcv, timeout=self.delay(), expected=msgs) self.ssn.acknowledge() def testSendAsyncCapacity0(self): diff --git a/qpid/python/qpid/tests/messaging/message.py b/qpid/python/qpid/tests/messaging/message.py index 930c031abb..654076588b 100644 --- a/qpid/python/qpid/tests/messaging/message.py +++ b/qpid/python/qpid/tests/messaging/message.py @@ -68,20 +68,7 @@ class MessageEchoTests(Base): def check(self, msg): self.snd.send(msg) echo = self.rcv.fetch(0) - - assert msg.id == echo.id - assert msg.subject == echo.subject - assert msg.user_id == echo.user_id - assert msg.to == echo.to - assert msg.reply_to == echo.reply_to - assert msg.correlation_id == echo.correlation_id - assert msg.durable == echo.durable - assert msg.priority == echo.priority - assert msg.ttl == echo.ttl - assert msg.properties == echo.properties - assert msg.content_type == echo.content_type - assert msg.content == echo.content, "%s, %s" % (msg, echo) - + self.assertEcho(msg, echo) self.ssn.acknowledge(echo) def testStringContent(self): diff --git a/qpid/python/qpid/validator.py b/qpid/python/qpid/validator.py index 7bd62b68f8..d234642b3e 100644 --- a/qpid/python/qpid/validator.py +++ b/qpid/python/qpid/validator.py @@ -54,6 +54,20 @@ class Types: else: return "%s is not one of: %s" % (o, ", ".join([t.__name__ for t in self.types])) +class List: + + def __init__(self, condition): + self.condition = condition + + def validate(self, o, ctx): + if not isinstance(o, list): + return "%s is not a list" % o + + ctx.push(o) + for v in o: + err = self.condition.validate(v, ctx) + if err: return err + class Map: def __init__(self, map, restricted=True): 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 0ac78a4799..9a4cfd37d6 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 @@ -430,6 +430,16 @@ class HeadersExchangeTests(TestHelper): self.myBasicPublish({"irrelevant":0}) self.assertEmpty(self.q) + def testMatchVoidValue(self): + self.session.exchange_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":None}) + self.myAssertPublishGet({"name":"fred"}) + self.myAssertPublishGet({"name":"bob"}) + + # Wont match + self.myBasicPublish({}) + self.myBasicPublish({"irrelevant":0}) + self.assertEmpty(self.q) + class MiscellaneousErrorsTests(TestHelper): """ diff --git a/qpid/tools/.gitignore b/qpid/tools/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/qpid/tools/.gitignore @@ -0,0 +1 @@ +/build |