diff options
author | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
---|---|---|
committer | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
commit | 633c33f224f3196f3f9bd80bd2e418d8143fea06 (patch) | |
tree | 1391da89470593209466df68c0b40b89c14963b1 /cpp | |
parent | c73f9286ebff93a6c8dbc29cf05e258c4b55c976 (diff) | |
download | qpid-python-633c33f224f3196f3f9bd80bd2e418d8143fea06.tar.gz |
QPID-3858: Updated branch - merged from trunk r.1333987
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1334037 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
277 files changed, 6731 insertions, 4656 deletions
diff --git a/cpp/bindings/qmf/ruby/qmf.rb b/cpp/bindings/qmf/ruby/qmf.rb index 34d3255d8d..9fbd50cbf6 100644 --- a/cpp/bindings/qmf/ruby/qmf.rb +++ b/cpp/bindings/qmf/ruby/qmf.rb @@ -26,18 +26,28 @@ module Qmf # Pull all the TYPE_* constants into Qmf namespace. Maybe there's an easier way? Qmfengine.constants.each do |c| + c = c.to_s if c.index('TYPE_') == 0 or c.index('ACCESS_') == 0 or c.index('DIR_') == 0 or c.index('CLASS_') == 0 or c.index('SEV_') == 0 const_set(c, Qmfengine.const_get(c)) end end + module StringHelpers + def ensure_encoding(str) + enc = (Encoding.default_external.name || "UTF-8" rescue "UTF-8") + str.respond_to?(:force_encoding) ? str.force_encoding(enc) : str + end + end + class Util + include StringHelpers + def qmf_to_native(val) case val.getType when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint when TYPE_UINT64 then val.asUint64 - when TYPE_SSTR, TYPE_LSTR then val.asString + when TYPE_SSTR, TYPE_LSTR then ensure_encoding(val.asString) when TYPE_ABSTIME then val.asInt64 when TYPE_DELTATIME then val.asUint64 when TYPE_REF then ObjectId.new(val.asObjectId) @@ -161,6 +171,7 @@ module Qmf ##============================================================================== class ConnectionSettings + include StringHelpers attr_reader :impl def initialize(url = nil) @@ -192,7 +203,7 @@ module Qmf def get_attr(key) _v = @impl.getAttr(key) if _v.isString() - return _v.asString() + return ensure_encoding(_v.asString()) elsif _v.isUint() return _v.asUint() elsif _v.isBool() @@ -348,7 +359,7 @@ module Qmf @broker = kwargs[:broker] if kwargs.include?(:broker) @allow_sets = :true - if cls: + if cls @event_class = cls @impl = Qmfengine::Event.new(@event_class.impl) elsif kwargs.include?(:impl) @@ -434,7 +445,7 @@ module Qmf @allow_sets = :false @broker = kwargs[:broker] if kwargs.include?(:broker) - if cls: + if cls @object_class = cls @impl = Qmfengine::Object.new(@object_class.impl) elsif kwargs.include?(:impl) @@ -707,6 +718,8 @@ module Qmf end class MethodResponse + include StringHelpers + def initialize(impl) @impl = Qmfengine::MethodResponse.new(impl) end @@ -720,7 +733,7 @@ module Qmf end def text - exception.asString + ensure_encoding(exception.asString) end def args @@ -885,6 +898,7 @@ module Qmf end class SchemaClassKey + include StringHelpers attr_reader :impl def initialize(i) @impl = Qmfengine::SchemaClassKey.new(i) @@ -899,7 +913,7 @@ module Qmf end def to_s - @impl.asString + ensure_encoding(@impl.asString) end end diff --git a/cpp/bindings/qmf/tests/run_interop_tests b/cpp/bindings/qmf/tests/run_interop_tests index 83e7f2593b..c370f211af 100755 --- a/cpp/bindings/qmf/tests/run_interop_tests +++ b/cpp/bindings/qmf/tests/run_interop_tests @@ -24,6 +24,7 @@ MY_DIR=`dirname \`which $0\`` QPID_DIR=${MY_DIR}/../../../.. BUILD_DIR=../../.. PYTHON_DIR=${QPID_DIR}/python +TOOLS_PY_DIR=${QPID_DIR}/tools/src/py QMF_DIR=${QPID_DIR}/extras/qmf QMF_DIR_PY=${QMF_DIR}/src/py BROKER_DIR=${BUILD_DIR}/src @@ -68,7 +69,7 @@ TESTS_FAILED=0 if test -d ${PYTHON_DIR} ; then start_broker echo "Running qmf interop tests using broker on port $BROKER_PORT" - PYTHONPATH=${PYTHON_DIR}:${QMF_DIR_PY}:${MY_DIR} + PYTHONPATH=${PYTHON_DIR}:${QMF_DIR_PY}:${MY_DIR}:${TOOLS_PY_DIR} export PYTHONPATH if test -d ${PYTHON_LIB_DIR} ; then diff --git a/cpp/bindings/qpid/Makefile.am b/cpp/bindings/qpid/Makefile.am index eaf45c2076..ae81696f27 100644 --- a/cpp/bindings/qpid/Makefile.am +++ b/cpp/bindings/qpid/Makefile.am @@ -35,7 +35,7 @@ if HAVE_PERL_DEVEL INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src -I$(PERL_INC) -EXTRA_DIST += perl/perl.i +EXTRA_DIST += perl/perl.i perl/CMakeLists.txt BUILT_SOURCES = perl/cqpid_perl.cpp SWIG_FLAGS = -w362,401 diff --git a/cpp/bindings/qpid/dotnet/Makefile.am b/cpp/bindings/qpid/dotnet/Makefile.am index f212a37bbe..82ae315578 100644 --- a/cpp/bindings/qpid/dotnet/Makefile.am +++ b/cpp/bindings/qpid/dotnet/Makefile.am @@ -80,7 +80,6 @@ EXTRA_DIST = configure-windows.ps1 \ examples/msvc9/csharp.map.callback.sender/csharp.map.callback.sender.csproj \ examples/msvc9/csharp.map.receiver/csharp.map.receiver.csproj \ examples/msvc9/csharp.map.sender/csharp.map.sender.csproj \ - extra_dist.txt \ Makefile.am \ msvc10/org.apache.qpid.messaging.sessionreceiver.sln \ msvc10/org.apache.qpid.messaging.sln \ @@ -93,7 +92,6 @@ EXTRA_DIST = configure-windows.ps1 \ src/AssemblyInfo.cpp \ src/Connection.cpp \ src/Connection.h \ - src/Duration.cpp \ src/Duration.h \ src/FailoverUpdates.cpp \ src/FailoverUpdates.h \ diff --git a/cpp/bindings/qpid/python/python.i b/cpp/bindings/qpid/python/python.i index 9d45bf54ee..a53cf3b853 100644 --- a/cpp/bindings/qpid/python/python.i +++ b/cpp/bindings/qpid/python/python.i @@ -132,7 +132,10 @@ static PyObject* pTransportFailure; # equivalent in C++, so we will translate them to sasl_mechanism # when possible. def __init__(self, url=None, **options): - args = [url] if url else [] + if url: + args = [url] + else: + args = [] if options : if "sasl_mechanisms" in options : if ' ' in options.get("sasl_mechanisms",'') : @@ -196,7 +199,7 @@ static PyObject* pTransportFailure; self._acknowledge_all(sync) __swig_getmethods__["connection"] = getConnection - if _newclass: connection = _swig_property(getConnection) + if _newclass: connection = property(getConnection) %} } @@ -205,10 +208,10 @@ static PyObject* pTransportFailure; %pythoncode %{ __swig_getmethods__["capacity"] = getCapacity __swig_setmethods__["capacity"] = setCapacity - if _newclass: capacity = _swig_property(getCapacity, setCapacity) + if _newclass: capacity = property(getCapacity, setCapacity) __swig_getmethods__["session"] = getSession - if _newclass: session = _swig_property(getSession) + if _newclass: session = property(getSession) %} %pythoncode %{ @@ -233,10 +236,10 @@ static PyObject* pTransportFailure; __swig_getmethods__["capacity"] = getCapacity __swig_setmethods__["capacity"] = setCapacity - if _newclass: capacity = _swig_property(getCapacity, setCapacity) + if _newclass: capacity = property(getCapacity, setCapacity) __swig_getmethods__["session"] = getSession - if _newclass: session = _swig_property(getSession) + if _newclass: session = property(getSession) %} } @@ -298,24 +301,23 @@ static PyObject* pTransportFailure; self.setContent(content) __swig_getmethods__["content"] = _get_content __swig_setmethods__["content"] = _set_content - if _newclass: content = _swig_property(_get_content, _set_content) + if _newclass: content = property(_get_content, _set_content) __swig_getmethods__["content_type"] = getContentType __swig_setmethods__["content_type"] = setContentType - if _newclass: content_type = _swig_property(getContentType, - setContentType) + if _newclass: content_type = property(getContentType, setContentType) __swig_getmethods__["id"] = getMessageId __swig_setmethods__["id"] = setMessageId - if _newclass: id = _swig_property(getMessageId, setMessageId) + if _newclass: id = property(getMessageId, setMessageId) __swig_getmethods__["subject"] = getSubject __swig_setmethods__["subject"] = setSubject - if _newclass: subject = _swig_property(getSubject, setSubject) + if _newclass: subject = property(getSubject, setSubject) __swig_getmethods__["priority"] = getPriority __swig_setmethods__["priority"] = setPriority - if _newclass: priority = _swig_property(getPriority, setPriority) + if _newclass: priority = property(getPriority, setPriority) def getTtl(self) : return self._getTtl().getMilliseconds()/1000.0 @@ -323,28 +325,26 @@ static PyObject* pTransportFailure; self._setTtl(Duration(int(1000*duration))) __swig_getmethods__["ttl"] = getTtl __swig_setmethods__["ttl"] = setTtl - if _newclass: ttl = _swig_property(getTtl, setTtl) + if _newclass: ttl = property(getTtl, setTtl) __swig_getmethods__["user_id"] = getUserId __swig_setmethods__["user_id"] = setUserId - if _newclass: user_id = _swig_property(getUserId, setUserId) + if _newclass: user_id = property(getUserId, setUserId) __swig_getmethods__["correlation_id"] = getCorrelationId __swig_setmethods__["correlation_id"] = setCorrelationId - if _newclass: correlation_id = _swig_property(getCorrelationId, - setCorrelationId) + if _newclass: correlation_id = property(getCorrelationId, setCorrelationId) __swig_getmethods__["redelivered"] = getRedelivered __swig_setmethods__["redelivered"] = setRedelivered - if _newclass: redelivered = _swig_property(getRedelivered, - setRedelivered) + if _newclass: redelivered = property(getRedelivered, setRedelivered) __swig_getmethods__["durable"] = getDurable __swig_setmethods__["durable"] = setDurable - if _newclass: durable = _swig_property(getDurable, setDurable) + if _newclass: durable = property(getDurable, setDurable) __swig_getmethods__["properties"] = getProperties - if _newclass: properties = _swig_property(getProperties) + if _newclass: properties = property(getProperties) def getReplyTo(self) : return self._getReplyTo().str() @@ -352,7 +352,7 @@ static PyObject* pTransportFailure; self._setReplyTo(Address(address_str)) __swig_getmethods__["reply_to"] = getReplyTo __swig_setmethods__["reply_to"] = setReplyTo - if _newclass: reply_to = _swig_property(getReplyTo, setReplyTo) + if _newclass: reply_to = property(getReplyTo, setReplyTo) def __repr__(self): args = [] diff --git a/cpp/bindings/qpid/ruby/README.rdoc b/cpp/bindings/qpid/ruby/README.rdoc index 0ae7e5cbed..478fc939d9 100644 --- a/cpp/bindings/qpid/ruby/README.rdoc +++ b/cpp/bindings/qpid/ruby/README.rdoc @@ -2,7 +2,7 @@ Qpid is an cross-platform enterprise messaging system. -Version :: 0.10.0.alpha.0 +Version :: 0.17.0 = Links diff --git a/cpp/bindings/qpid/ruby/Rakefile b/cpp/bindings/qpid/ruby/Rakefile index df0b3970b6..99c3e13c83 100644 --- a/cpp/bindings/qpid/ruby/Rakefile +++ b/cpp/bindings/qpid/ruby/Rakefile @@ -120,7 +120,9 @@ spec = Gem::Specification.new do |s| "lib/**/*.rb", "test/**/*.rb", "examples/**/*.rb", - "ext/**/*"] + "ext/**/*", + "features/**/*", + "spec/**/*"] end Gem::PackageTask.new(spec) do |pkg| diff --git a/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb index e5071ca4e6..845cc2b116 100644 --- a/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb +++ b/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb @@ -1,3 +1,22 @@ +# +# 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. +# + Given /^an Address with the name "([^"]*)" and subject "([^"]*)" and option "([^"]*)" set to "([^"]*)"$/ do |name, subject, key, value| options = Hash.new options["#{key}"] = "#{value}" diff --git a/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb index b4146ac1fb..3fe3e6941f 100644 --- a/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb +++ b/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb @@ -1,3 +1,22 @@ +# +# 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. +# + # close all connections After do @connection.close if @connection diff --git a/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb index 8157fb7735..a8c8aa4a43 100644 --- a/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb +++ b/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb @@ -1,3 +1,22 @@ +# +# 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. +# + Given /^an existing receiver for "([^"]*)"$/ do |address| steps %Q{ Given an open session diff --git a/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb index 3ff081c7d2..93dbd2d5c0 100644 --- a/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb +++ b/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb @@ -1,3 +1,22 @@ +# +# 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. +# + Given /^the message "([^"]*)" is sent$/ do |content| @sender.send Qpid::Messaging::Message.new :content => "#{content}" end diff --git a/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb index f97e423ee9..cf775d917d 100644 --- a/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb +++ b/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb @@ -1,3 +1,22 @@ +# +# 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. +# + Given /^a closed session/ do steps %Q{ Given an open connection diff --git a/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb b/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb index 7b9130156d..2f20fab18e 100644 --- a/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb +++ b/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb @@ -45,8 +45,10 @@ module Qpid content_type = message.content_type unless content_type case content_type - when "amqp/map": Cqpid.decodeMap message.message_impl - when "amqp/list": Cqpid.decodeList message.message_impl + when "amqp/map" + Cqpid.decodeMap message.message_impl + when "amqp/list" + Cqpid.decodeList message.message_impl end message.content diff --git a/cpp/bindings/qpid/ruby/lib/qpid/version.rb b/cpp/bindings/qpid/ruby/lib/qpid/version.rb index f387ba98dc..39524e428f 100644 --- a/cpp/bindings/qpid/ruby/lib/qpid/version.rb +++ b/cpp/bindings/qpid/ruby/lib/qpid/version.rb @@ -22,7 +22,7 @@ module Qpid module Version NUMBERS = [MAJOR = 0, - MINOR = 13, + MINOR = 17, BUILD = 0] end diff --git a/cpp/bindings/swig_python_typemaps.i b/cpp/bindings/swig_python_typemaps.i index e99ce65254..25a4e46b18 100644 --- a/cpp/bindings/swig_python_typemaps.i +++ b/cpp/bindings/swig_python_typemaps.i @@ -25,7 +25,11 @@ static PyObject* pUuidModule; %} %init %{ - pUuidModule = PyImport_ImportModule("uuid"); + /* Instead of directly referencing the uuid module (which is not available + * on older versions of Python), reference the wrapper defined in + * qpid.datatypes. + */ + pUuidModule = PyImport_ImportModule("qpid.datatypes"); /* Although it is not required, we'll publish the uuid module in our * module, as if this module was a python module and we called diff --git a/cpp/configure.ac b/cpp/configure.ac index 8729ace169..c68fed932e 100644 --- a/cpp/configure.ac +++ b/cpp/configure.ac @@ -27,7 +27,7 @@ AC_PROG_CC_STDC AM_PROG_CC_C_O AC_PROG_CXX AC_USE_SYSTEM_EXTENSIONS -AC_LANG([C++]) +AC_LANG([C++]) # Check for optional use of help2man AC_CHECK_PROG([HELP2MAN], [help2man], [help2man]) @@ -83,14 +83,14 @@ if test x$GXX = xyes; then gl_COMPILER_FLAGS(-Wvolatile-register-var) gl_COMPILER_FLAGS(-Winvalid-pch) gl_COMPILER_FLAGS(-Wno-system-headers) - gl_COMPILER_FLAGS(-Woverloaded-virtual) + gl_COMPILER_FLAGS(-Woverloaded-virtual) AC_SUBST([WARNING_CFLAGS], [$COMPILER_FLAGS]) AC_DEFINE([lint], 1, [Define to 1 if the compiler is checking for lint.]) COMPILER_FLAGS= fi else AC_CHECK_DECL([__SUNPRO_CC], [SUNCC=yes], [SUNCC=no]) - + # Set up for sun CC compiler if test x$SUNCC = xyes; then if test "${enableval}" = yes; then @@ -118,7 +118,7 @@ LIBS=$gl_saved_libs gl_CLOCK_TIME -# Enable Valgrind +# Enable Valgrind AC_ARG_ENABLE([valgrind], [AS_HELP_STRING([--enable-valgrind], [run valgrind memory checker on tests, if available (default yes)])], @@ -203,13 +203,26 @@ AM_CONDITIONAL([HAVE_RUBY_DEVEL], [test -f $RUBY_INC/ruby.h && test -n "$SWIG"]) # Python bindings: To build python wrappers, the python-devel files must be present. AM_PATH_PYTHON() AS_IF([test -n "$PYTHON"], [ - PKG_CHECK_MODULES([PYTHON], [python], [have_python_dev=yes],[ + PKG_CHECK_MODULES([PYTHON], [python-$PYTHON_VERSION], [have_python_dev=yes],[ + # We didn't find pkg-config information for python-2.7 this + # may mean we have an earlier python version: + # Try to find the include and library files directly in the default + # location + AC_MSG_WARN([Didn't find Python 2.7 developer libs - looking for older version]) + PYTHON_INC=$($PYTHON -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') + AC_CHECK_LIB([python$PYTHON_VERSION],[Py_Initialize]) + AC_CHECK_FILE(["$PYTHON_INC/Python.h"],[ + PYTHON_CFLAGS="-I$PYTHON_INC" + PYTHON_LIBS="-lpython$PYTHON_VERSION" + have_python_dev=yes + ],[ if test yes = "$with_python" ; then AC_MSG_ERROR([Couldn't find Python developer libs - you probably need to install a python-dev or python-devel package]) else AC_MSG_WARN([Couldn't find Python developer libs - you probably don't have a python-dev or python-devel package installed]) fi ]) + ]) AC_SUBST(PYTHON_CFLAGS) AC_SUBST(PYTHON_LIBS) ]) @@ -249,7 +262,7 @@ tmp_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -L/usr/lib/openais -L/usr/lib64/openais -L/usr/lib/corosync -L/usr/lib64/corosync" AC_CHECK_LIB([cpg],[cpg_local_get],[have_libcpg=yes],) AC_CHECK_HEADERS([openais/cpg.h corosync/cpg.h],[have_cpg_h=yes],) -AC_ARG_WITH([cpg], +AC_ARG_WITH([cpg], [AS_HELP_STRING([--with-cpg], [Build with CPG support for clustering.])], [case "${withval}" in yes) # yes - require dependencies @@ -257,7 +270,7 @@ AC_ARG_WITH([cpg], test x$have_cpg_h = xyes || AC_MSG_ERROR([cpg.h not found, install openais-devel or corosync-devel]) with_cpg=yes ;; - no) with_cpg=no ;; + no) with_cpg=no ;; *) AC_MSG_ERROR([Bad value ${withval} for --with-cpg option]) ;; esac], [ # not specified - use if present @@ -265,12 +278,14 @@ AC_ARG_WITH([cpg], ] ) AM_CONDITIONAL([HAVE_LIBCPG], [test x$with_cpg = xyes]) +AC_SUBST(USE_CPG, [$with_cpg]) + # Clean up unnceccassary flags if we don't use clustering AS_IF([test ! x$with_cpg = xyes], [LDFLAGS=$tmp_LDFLAGS]) AC_CHECK_LIB([cman],[cman_is_quorate],have_libcman=yes,) AC_CHECK_HEADERS([libcman.h],have_libcman_h=yes,) -AC_ARG_WITH([libcman], +AC_ARG_WITH([libcman], [AS_HELP_STRING([--with-libcman], [Integration with libcman quorum service.])], [case "${withval}" in yes) # yes - require dependencies @@ -278,7 +293,7 @@ AC_ARG_WITH([libcman], test x$have_libcman_h = xyes || AC_MSG_ERROR([libcman.h not found, install cman-devel or cmanlib-devel]) with_libcman=yes ;; - no) with_libcman=no ;; + no) with_libcman=no ;; *) AC_MSG_ERROR([Bad value ${withval} for --with-libcman option]) ;; esac], [ # not specified - use if present and we're using with_cpg @@ -430,9 +445,9 @@ AC_ARG_WITH([ssl], [ with_SSL=yes AC_PATH_PROG([NSPR_CONFIG], [nspr-config]) - AS_IF([test x$NSPR_CONFIG = x], [with_SSL=no], + AS_IF([test x$NSPR_CONFIG = x], [with_SSL=no], [AC_PATH_PROG([NSS_CONFIG], [nss-config]) - AS_IF([test x$NSS_CONFIG = x], [with_SSL=no], + AS_IF([test x$NSS_CONFIG = x], [with_SSL=no], [SSL_CFLAGS="`$NSPR_CONFIG --cflags` `$NSS_CONFIG --cflags`" SSL_LDFLAGS="`$NSPR_CONFIG --libs` `$NSS_CONFIG --libs`"])]) ] @@ -448,7 +463,7 @@ AC_ARG_WITH([poller], [AS_HELP_STRING([--with-poller], [The low level poller implementation: poll/solaris-ecf/epoll])], [case ${withval} in poll) - AC_CHECK_HEADERS([sys/poll.h],[poller=no],[AC_MSG_ERROR([Can't find poll.h header file for poll])]) + AC_CHECK_HEADERS([sys/poll.h],[poller=poll],[AC_MSG_ERROR([Can't find poll.h header file for poll])]) ;; solaris-ecf) AC_CHECK_HEADERS([port.h],[poller=solaris-ecf],[AC_MSG_ERROR([Can't find port.h header file for solaris-ecf])]) @@ -458,14 +473,17 @@ AC_ARG_WITH([poller], ;; esac], [ - AC_CHECK_HEADERS([sys/poll.h],[poller=no],) - AC_CHECK_HEADERS([port.h],[poller=solaris-ecf],) + # We check for poll first so that it is overridden + AC_CHECK_HEADERS([sys/poll.h],[poller=poll],) + # Not currently supported - WIP + #AC_CHECK_HEADERS([port.h],[poller=solaris-ecf],) AC_CHECK_HEADERS([sys/epoll.h],[poller=epoll],) ] ) -AM_CONDITIONAL([HAVE_ECF], [test x$poller = xsolaris-ecf]) -AM_CONDITIONAL([HAVE_EPOLL], [test x$poller = xepoll]) +AM_CONDITIONAL([USE_ECF], [test x$poller = xsolaris-ecf]) +AM_CONDITIONAL([USE_POLL], [test x$poller = xpoll]) +AM_CONDITIONAL([USE_EPOLL], [test x$poller = xepoll]) #Filter not implemented or invalid mechanisms if test $poller = xno; then @@ -480,12 +498,30 @@ case "$host" in esac AM_CONDITIONAL([SUNOS], [test x$arch = xsolaris]) +# Check whether we've got the header for dtrace static probes +AC_ARG_WITH([probes], + [AS_HELP_STRING([--with-probes], [Build with dtrace/systemtap static probes])], + [case ${withval} in + yes) + AC_CHECK_HEADERS([sys/sdt.h]) + ;; + no) + ;; + *) + AC_MSG_ERROR([Bad value for --with-probes: ${withval}]) + ;; + esac], + [ + AC_CHECK_HEADERS([sys/sdt.h]) + ] +) + # Check for some syslog capabilities not present in all systems -AC_TRY_COMPILE([#include <sys/syslog.h>], +AC_TRY_COMPILE([#include <sys/syslog.h>], [int v = LOG_AUTHPRIV;], [AC_DEFINE([HAVE_LOG_AUTHPRIV], [1], [Set to 1 whether LOG_AUTHPRIV is supported.])],) -AC_TRY_COMPILE([#include <sys/syslog.h>], +AC_TRY_COMPILE([#include <sys/syslog.h>], [int v = LOG_FTP;], [AC_DEFINE([HAVE_LOG_FTP], [1], [Set to 1 whether LOG_FTP is supported.])],) @@ -503,8 +539,6 @@ AC_CHECK_LIB([nsl],[getipnodebyname],[NSL_LIB="-lnsl"],[NSL_LIB=""],[]) SOCKLIBS="$SOCKET_LIB $NSL_LIB" AC_SUBST([SOCKLIBS]) -AM_PATH_PYTHON() - # Used by env scripts to find libraries in cmake or autoconf builds. builddir_lib_suffix="/.libs" AC_SUBST([builddir_lib_suffix]) @@ -539,6 +573,7 @@ AC_CONFIG_FILES([ managementgen/Makefile etc/Makefile src/Makefile + src/tests/cpg_check.sh src/tests/Makefile src/tests/test_env.sh src/tests/install_env.sh diff --git a/cpp/design_docs/new-ha-design.txt b/cpp/design_docs/new-ha-design.txt index 3272b1315f..acca1720b4 100644 --- a/cpp/design_docs/new-ha-design.txt +++ b/cpp/design_docs/new-ha-design.txt @@ -257,20 +257,24 @@ Broker startup with store: - When connecting as backup, check UUID matches primary, shut down if not. - Empty: start ok, no UUID check with primary. -** Current Limitations +* Current Limitations (In no particular order at present) For message replication: -LM1 - The re-synchronisation does not handle the case where a newly elected -primary is *behind* one of the other backups. To address this I propose -a new event for restting the sequence that the new primary would send -out on detecting that a replicating browser is ahead of it, requesting -that the replica revert back to a particular sequence number. The -replica on receiving this event would then discard (i.e. dequeue) all -the messages ahead of that sequence number and reset the counter to -correctly sequence any subsequently delivered messages. +LM1a - On failover, backups delete their queues and download the full queue state from the +primary. There was code to use messags already on the backup for re-synchronisation, it +was removed in early development (r1214490) to simplify the logic while getting basic +replication working. It needs to be re-introduced. + +LM1b - This re-synchronisation does not handle the case where a newly elected primary is *behind* +one of the other backups. To address this I propose a new event for restting the sequence +that the new primary would send out on detecting that a replicating browser is ahead of +it, requesting that the replica revert back to a particular sequence number. The replica +on receiving this event would then discard (i.e. dequeue) all the messages ahead of that +sequence number and reset the counter to correctly sequence any subsequently delivered +messages. LM2 - There is a need to handle wrap-around of the message sequence to avoid confusing the resynchronisation where a replica has been disconnected @@ -288,12 +292,12 @@ confirmed to the publisher before they are replicated, leaving them vulnerable to a loss of the new primary before they are replicated. LM6 - persistence: In the event of a total cluster failure there are -no tools to automatically identify the "latest" store. Once this -is manually identfied, all other stores belonging to cluster members -must be erased and the latest node must started as primary. +no tools to automatically identify the "latest" store. -LM6 - persistence: In the event of a single node failure, that nodes -store must be erased before it can re-join the cluster. +LM7 - persistence: In the event of a persistent broker being +re-started (due to failure or admin) it should be able to use its +stored messages to reduce the download required from the +primary. This means storing message IDs persistently. For configuration propagation: @@ -349,6 +353,12 @@ LC6 - The events and query responses are not fully synchronized. It is not possible to miss a create event and yet not to have the object in question in the query response however. +LC7 Federated links from the primary will be lost in failover, they will not be re-connected on +the new primary. Federation links to the primary can fail over. + +LC8 Only plain FIFO queues can be replicated. LVQs and ring queues are not yet supported. + +LC9 The "last man standing" feature of the old cluster is not available. * Benefits compared to previous cluster implementation. @@ -359,58 +369,3 @@ LC6 - The events and query responses are not fully synchronized. - Can take advantage of resource manager features, e.g. virtual IP addresses. - Fewer inconsistent errors (store failures) that can be handled without killing brokers. - Improved performance -* User Documentation Notes - -Notes to seed initial user documentation. Loosely tracking the implementation, -some points mentioned in the doc may not be implemented yet. - -** High Availability Overview - -HA is implemented using a 'hot standby' approach. Clients are directed -to a single "primary" broker. The primary executes client requests and -also replicates them to one or more "backup" brokers. If the primary -fails, one of the backups takes over the role of primary carrying on -from where the primary left off. Clients will fail over to the new -primary automatically and continue their work. - -TODO: at least once, deduplication. - -** Enabling replication on the client. - -To enable replication set the qpid.replicate argument when creating a -queue or exchange. - -This can have one of 3 values -- none: the object is not replicated -- configuration: queues, exchanges and bindings are replicated but messages are not. -- messages: configuration and messages are replicated. - -TODO: examples -TODO: more options for default value of qpid.replicate - -A HA client connection has multiple addresses, one for each broker. If -the it fails to connect to an address, or the connection breaks, -it will automatically fail-over to another address. - -Only the primary broker accepts connections, the backup brokers -redirect connection attempts to the primary. If the primary fails, one -of the backups is promoted to primary and clients fail-over to the new -primary. - -TODO: using multiple-address connections, examples c++, python, java. - -TODO: dynamic cluster addressing? - -TODO: need de-duplication. - -** Enabling replication on the broker. - -Network topology: backup links, separate client/broker networks. -Describe failover mechanisms. -- Client view: URLs, failover, exclusion & discovery. -- Broker view: similar. -Role of rmganager - -** Configuring rgmanager - -** Configuring qpidd diff --git a/cpp/etc/Makefile.am b/cpp/etc/Makefile.am index 1e4db561a7..b154a105d4 100644 --- a/cpp/etc/Makefile.am +++ b/cpp/etc/Makefile.am @@ -20,9 +20,10 @@ SASL_CONF = sasl2/qpidd.conf EXTRA_DIST = \ $(SASL_CONF) \ - qpidd qpidd.conf qpidc.conf CMakeLists.txt + qpidd qpidd-primary qpidd.conf qpidc.conf CMakeLists.txt \ + cluster.conf-example.xml -confdir=$(sysconfdir)/qpid +confdir = $(sysconfdir)/qpid nobase_conf_DATA=\ qpidc.conf @@ -32,5 +33,23 @@ nobase_sysconf_DATA = \ if HAVE_SASL nobase_sysconf_DATA += \ $(SASL_CONF) - endif + +# Substitute values for directories in init scripts. +# +# We can't use autoconf substitution directly because it leaves +# ${prefix} and ${exec_prefix} unexpanded. Substitute with sed +# scripts. +SUBST="s|!!sysconfdir!!|${sysconfdir}|;s|!!sbindir!!|${sbindir}|;s|!!bindir!!|${bindir}|" +qpidd: qpidd.in + sed $(SUBST) $< > $@ +qpidd-primary: qpidd-primary.in + sed $(SUBST) $< > $@ +cluster.conf-example.xml: cluster.conf-example.xml.in + sed $(SUBST) $< > $@ + +CLEANFILES = qpidd qpidd-primary cluster.conf-example.xml + +initddir = $(sysconfdir)/init.d +nobase_initd_SCRIPTS = qpidd qpidd-primary + diff --git a/cpp/etc/cluster.conf-example.xml.in b/cpp/etc/cluster.conf-example.xml.in new file mode 100644 index 0000000000..14b961a363 --- /dev/null +++ b/cpp/etc/cluster.conf-example.xml.in @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- +This is an example of a cluster.conf file to run qpidd HA under rgmanager. +This example assumes a 3 node cluster, with nodes named node1, node2 and node3. + +NOTE: fencing is not shown, it should be configured in a real cluster configuration. +--> + +<cluster name="qpid-test" config_version="18"> + <!-- The cluster has 3 nodes. Each has a unique nodid and one vote + for quorum. --> + <clusternodes> + <clusternode name="node1.example.com" nodeid="1"/> + <clusternode name="node2.example.com" nodeid="2"/> + <clusternode name="node3.example.com" nodeid="3"/> + </clusternodes> + <!-- Resouce Manager configuration. --> + <rm> + <!-- + There is a failoverdomain for each node containing just that node. + This lets us stipulate that the qpidd service should always run on each node. + --> + <failoverdomains> + <failoverdomain name="node1-domain" restricted="1"> + <failoverdomainnode name="node1.example.com"/> + </failoverdomain> + <failoverdomain name="node2-domain" restricted="1"> + <failoverdomainnode name="node2.example.com"/> + </failoverdomain> + <failoverdomain name="node3-domain" restricted="1"> + <failoverdomainnode name="node3.example.com"/> + </failoverdomain> + </failoverdomains> + + <resources> + <!-- This script starts a qpidd broker acting as a backup. --> + <script file="!!sysconfdir!!/init.d/qpidd" name="qpidd"/> + + <!-- This script promotes the qpidd broker on this node to primary. --> + <script file="!!sysconfdir!!/init.d/qpidd-primary" name="qpidd-primary"/> + + <!-- This is a virtual IP address for broker replication traffic. --> + <ip address="20.0.10.200" monitor_link="1"/> + + <!-- This is a virtual IP address on a seprate network for client traffic. --> + <ip address="20.0.20.200" monitor_link="1"/> + </resources> + + <!-- There is a qpidd service on each node, it should be restarted if it fails. --> + <service name="node1-qpidd-service" domain="node1-domain" recovery="restart"> + <script ref="qpidd"/> + </service> + <service name="node2-qpidd-service" domain="node2-domain" recovery="restart"> + <script ref="qpidd"/> + </service> + <service name="node3-qpidd-service" domain="node3-domain" recovery="restart"> + <script ref="qpidd"/> + </service> + + <!-- There should always be a single qpidd-primary service, it can run on any node. --> + <service name="qpidd-primary-service" autostart="1" exclusive="0" recovery="relocate"> + <script ref="qpidd-primary"/> + <!-- The primary has the IP addresses for brokers and clients to connect. --> + <ip ref="20.0.10.200"/> + <ip ref="20.0.20.200"/> + </service> + </rm> + <fencedevices/> + <fence_daemon clean_start="0" post_fail_delay="0" post_join_delay="3"/> +</cluster> diff --git a/cpp/etc/qpidd-primary.in b/cpp/etc/qpidd-primary.in new file mode 100755 index 0000000000..39700bead3 --- /dev/null +++ b/cpp/etc/qpidd-primary.in @@ -0,0 +1,102 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +# qpidd Startup script for the Qpid messaging daemon. +# + +### BEGIN INIT INFO +# Provides: qpidd-primary +# Required-Start: $qpidd +# Required-Stop: $qpidd +# Default-Start: +# Default-Stop: 0 1 2 3 4 5 6 +# Short-Description: promote qpidd to cluster primary +# Description: Qpidd can be run in an active/passive cluster. Promote a running qpidd to primary. +### END INIT INFO + +# chkconfig: - 85 15 +# description: Qpidd can be run in an active/passive cluster. Promote a running qpidd to primary. +# processname: qpidd + +prog=qpidd + +# Source function library. +. /etc/rc.d/init.d/functions + +if [ -f !!sysconfdir!!/sysconfig/$prog ] ; then + . !!sysconfdir!!/sysconfig/$prog +fi + +# The following variables can be overridden in !!sysconfdir!!/sysconfig/$prog +[[ $QPID_PORT ]] || QPID_PORT=5672 +[[ $QPID_HA ]] || QPID_HA=!!bindir!!/qpid-ha +export QPID_PORT + +RETVAL=0 + +#ensure binary is present and executable +if [[ !(-x $QPID_HA) ]]; then + echo "qpid-ha executable not found or not executable" +fi + +status() { + if $QPID_HA -b localhost:$QPID_PORT status --expect=primary ; then + echo "qpidd is primary" + else + echo "qpidd is not primary" + return 1 + fi +} + +start() { + service qpidd start + echo -n $"Promoting qpid daemon to cluster primary: " + $QPID_HA -b localhost:$QPID_PORT promote + [ "$?" -eq 0 ] && success || failure +} + +stop() { + service qpidd stop +} + +reload() { + echo 1>&2 $"$0: reload not supported" + exit 3 +} + +restart() { + service qpidd restart && start +} + +# See how we were called. +case "$1" in + start|stop|status|restart|reload) + $1 + RETVAL=$? + ;; + force-reload) + restart + ;; + *) + echo 1>&2 $"Usage: $0 {start|stop|status|restart|force-reload}" + exit 2 +esac + +exit $RETVAL diff --git a/cpp/etc/qpidd b/cpp/etc/qpidd.in index 07cbb825d5..2b43625b7d 100755 --- a/cpp/etc/qpidd +++ b/cpp/etc/qpidd.in @@ -39,18 +39,19 @@ prog=qpidd lockfile=/var/lock/subsys/$prog pidfile=/var/run/qpidd.pid +# Source configuration +if [ -f !!sysconfdir!!/sysconfig/$prog ] ; then + . !!sysconfdir!!/sysconfig/$prog +fi + # Source function library. . /etc/rc.d/init.d/functions -if [ -f /etc/sysconfig/$prog ] ; then - . /etc/sysconfig/$prog -fi - RETVAL=0 #ensure binary is present and executable -if [[ !(-x /usr/sbin/$prog) ]] ; then - echo "/usr/sbin/$prog not found or not executable" +if [[ !(-x !!sbindir!!/$prog) ]] ; then + echo "!!sbindir!!/$prog not found or not executable" exit 5 fi @@ -64,7 +65,7 @@ fi start() { [[ $QPID_DATA_DIR ]] || QPID_DATA_DIR=/var/lib/qpidd echo -n $"Starting Qpid AMQP daemon: " - daemon --pidfile $pidfile --check $prog --user qpidd /usr/sbin/$prog --data-dir $QPID_DATA_DIR --daemon $QPIDD_OPTIONS + daemon --pidfile $pidfile --check $prog --user qpidd !!sbindir!!/$prog --data-dir $QPID_DATA_DIR --daemon $QPIDD_OPTIONS RETVAL=$? echo [ $RETVAL = 0 ] && touch $lockfile @@ -72,7 +73,7 @@ start() { touch $pidfile chown qpidd.qpidd $pidfile [ -x /sbin/restorecon ] && /sbin/restorecon $pidfile - runuser - -s /bin/sh qpidd -c "/usr/sbin/$prog --check > $pidfile" + runuser - -s /bin/sh qpidd -c "!!sbindir!!/$prog --check > $pidfile" fi return $RETVAL } diff --git a/cpp/include/qpid/Options.h b/cpp/include/qpid/Options.h index 63d91c2d72..9860076195 100644 --- a/cpp/include/qpid/Options.h +++ b/cpp/include/qpid/Options.h @@ -163,10 +163,12 @@ struct Options : public po::options_description { */ struct CommonOptions : public Options { QPID_COMMON_EXTERN CommonOptions(const std::string& name=std::string(), - const std::string& configfile=std::string()); + const std::string& configfile=std::string(), + const std::string& clientConfigFile=std::string()); bool help; bool version; std::string config; + std::string clientConfig; }; diff --git a/cpp/include/qpid/Url.h b/cpp/include/qpid/Url.h index 915b08ac5f..b3ff9576e2 100644 --- a/cpp/include/qpid/Url.h +++ b/cpp/include/qpid/Url.h @@ -39,7 +39,7 @@ struct Url : public std::vector<Address> { * on a multi-homed host. */ QPID_COMMON_EXTERN static Url getIpAddressesUrl(uint16_t port); - struct Invalid : public Exception { Invalid(const std::string& s); }; + struct Invalid : public Exception { QPID_COMMON_EXTERN Invalid(const std::string& s); }; /** Convert to string form. */ QPID_COMMON_EXTERN std::string str() const; @@ -52,6 +52,8 @@ struct Url : public std::vector<Address> { /** Parse url, throw Invalid if invalid. */ explicit Url(const std::string& url) { parse(url.c_str()); } + /** Parse url, throw Invalid if invalid. */ + explicit Url(const std::string& url, const std::string& defaultProtocol) { parse(url.c_str(), defaultProtocol); } /** Parse url, throw Invalid if invalid. */ explicit Url(const char* url) { parse(url); } @@ -66,10 +68,12 @@ struct Url : public std::vector<Address> { *@exception Invalid if the url is invalid. */ QPID_COMMON_EXTERN void parse(const char* url); + QPID_COMMON_EXTERN void parse(const char* url, const std::string& defaultProtocol); QPID_COMMON_INLINE_EXTERN void parse(const std::string& url) { parse(url.c_str()); } /** Replace contesnts with parsed URL. Replace with empty URL if invalid. */ QPID_COMMON_EXTERN void parseNoThrow(const char* url); + QPID_COMMON_EXTERN void parseNoThrow(const char* url, const std::string& defaultProtocol); /** Add a protocol tag to be recognzed in URLs. * Only for use by protcol plug-in initializers. diff --git a/cpp/include/qpid/framing/Array.h b/cpp/include/qpid/framing/Array.h index 1e97be3bb4..4f82d4dbf0 100644 --- a/cpp/include/qpid/framing/Array.h +++ b/cpp/include/qpid/framing/Array.h @@ -1,3 +1,6 @@ +#ifndef QPID_FRAMING_ARRAY_H +#define QPID_FRAMING_ARRAY_H + /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -18,21 +21,22 @@ * under the License. * */ + #include "qpid/framing/amqp_types.h" -#include "qpid/framing/FieldValue.h" #include "qpid/framing/TypeCode.h" + #include <boost/shared_ptr.hpp> + #include <iostream> #include <vector> -#include "qpid/CommonImportExport.h" -#ifndef _Array_ -#define _Array_ +#include "qpid/CommonImportExport.h" namespace qpid { namespace framing { class Buffer; +class FieldValue; class QPID_COMMON_CLASS_EXTERN Array { @@ -75,12 +79,10 @@ class QPID_COMMON_CLASS_EXTERN Array // Non-std interface QPID_COMMON_INLINE_EXTERN void add(ValuePtr value) { push_back(value); } - template <class T> - void collect(std::vector<T>& out) const - { - for (ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) { - out.push_back((*i)->get<T>()); - } + // For use in standard algorithms + template <typename R, typename V> + static R get(const V& v) { + return v->get<R>(); } private: diff --git a/cpp/include/qpid/framing/FieldTable.h b/cpp/include/qpid/framing/FieldTable.h index bdcef6d7fd..1986a72d10 100644 --- a/cpp/include/qpid/framing/FieldTable.h +++ b/cpp/include/qpid/framing/FieldTable.h @@ -1,3 +1,6 @@ +#ifndef _FieldTable_ +#define _FieldTable_ + /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -18,15 +21,17 @@ * under the License. * */ -#include <iostream> -#include <vector> + +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" + #include <boost/shared_ptr.hpp> +#include <boost/shared_array.hpp> + +#include <iosfwd> #include <map> -#include "qpid/framing/amqp_types.h" -#include "qpid/CommonImportExport.h" -#ifndef _FieldTable_ -#define _FieldTable_ +#include "qpid/CommonImportExport.h" namespace qpid { /** @@ -56,10 +61,10 @@ class FieldTable typedef ValueMap::reference reference; typedef ValueMap::value_type value_type; - QPID_COMMON_INLINE_EXTERN FieldTable() {}; - QPID_COMMON_EXTERN FieldTable(const FieldTable& ft); - QPID_COMMON_EXTERN ~FieldTable(); - QPID_COMMON_EXTERN FieldTable& operator=(const FieldTable& ft); + QPID_COMMON_EXTERN FieldTable(); + QPID_COMMON_EXTERN FieldTable(const FieldTable&); + QPID_COMMON_EXTERN FieldTable& operator=(const FieldTable&); + // Compiler default destructor fine QPID_COMMON_EXTERN uint32_t encodedSize() const; QPID_COMMON_EXTERN void encode(Buffer& buffer) const; QPID_COMMON_EXTERN void decode(Buffer& buffer); @@ -91,32 +96,35 @@ class FieldTable QPID_COMMON_EXTERN bool getArray(const std::string& name, Array& value) const; QPID_COMMON_EXTERN bool getFloat(const std::string& name, float& value) const; QPID_COMMON_EXTERN bool getDouble(const std::string& name, double& value) const; - //bool getTimestamp(const std::string& name, uint64_t& value) const; - //bool getDecimal(string& name, xxx& value); + //QPID_COMMON_EXTERN bool getTimestamp(const std::string& name, uint64_t& value) const; + //QPID_COMMON_EXTERN bool getDecimal(string& name, xxx& value); QPID_COMMON_EXTERN void erase(const std::string& name); QPID_COMMON_EXTERN bool operator==(const FieldTable& other) const; // Map-like interface. - ValueMap::const_iterator begin() const { return values.begin(); } - ValueMap::const_iterator end() const { return values.end(); } - ValueMap::const_iterator find(const std::string& s) const { return values.find(s); } + QPID_COMMON_EXTERN ValueMap::const_iterator begin() const; + QPID_COMMON_EXTERN ValueMap::const_iterator end() const; + QPID_COMMON_EXTERN ValueMap::const_iterator find(const std::string& s) const; - ValueMap::iterator begin() { return values.begin(); } - ValueMap::iterator end() { return values.end(); } - ValueMap::iterator find(const std::string& s) { return values.find(s); } + QPID_COMMON_EXTERN ValueMap::iterator begin(); + QPID_COMMON_EXTERN ValueMap::iterator end(); + QPID_COMMON_EXTERN ValueMap::iterator find(const std::string& s); QPID_COMMON_EXTERN std::pair <ValueMap::iterator, bool> insert(const ValueMap::value_type&); QPID_COMMON_EXTERN ValueMap::iterator insert(ValueMap::iterator, const ValueMap::value_type&); - void clear() { values.clear(); } - - // ### Hack Alert - - ValueMap::iterator getValues() { return values.begin(); } + QPID_COMMON_EXTERN void clear(); private: - ValueMap values; + void realDecode() const; + void flushRawCache(); + + mutable qpid::sys::Mutex lock; + mutable ValueMap values; + mutable boost::shared_array<uint8_t> cachedBytes; + mutable uint32_t cachedSize; // if = 0 then non cached size as 0 is not a legal size + mutable bool newBytes; QPID_COMMON_EXTERN friend std::ostream& operator<<(std::ostream& out, const FieldTable& body); }; diff --git a/cpp/include/qpid/framing/SequenceNumber.h b/cpp/include/qpid/framing/SequenceNumber.h index eed15a4b75..dd85d97a52 100644 --- a/cpp/include/qpid/framing/SequenceNumber.h +++ b/cpp/include/qpid/framing/SequenceNumber.h @@ -52,9 +52,9 @@ boost::equality_comparable< uint32_t getValue() const { return uint32_t(value); } operator uint32_t() const { return uint32_t(value); } - void encode(Buffer& buffer) const; - void decode(Buffer& buffer); - uint32_t encodedSize() const; + QPID_COMMON_EXTERN void encode(Buffer& buffer) const; + QPID_COMMON_EXTERN void decode(Buffer& buffer); + QPID_COMMON_EXTERN uint32_t encodedSize() const; template <class S> void serialize(S& s) { s(value); } diff --git a/cpp/include/qpid/framing/SequenceSet.h b/cpp/include/qpid/framing/SequenceSet.h index 0a78e418ba..827c8999b3 100644 --- a/cpp/include/qpid/framing/SequenceSet.h +++ b/cpp/include/qpid/framing/SequenceSet.h @@ -38,9 +38,9 @@ class QPID_COMMON_CLASS_EXTERN SequenceSet : public RangeSet<SequenceNumber> { SequenceSet(const SequenceNumber& start, const SequenceNumber finish) { add(start,finish); } - void encode(Buffer& buffer) const; - void decode(Buffer& buffer); - uint32_t encodedSize() const; + QPID_COMMON_EXTERN void encode(Buffer& buffer) const; + QPID_COMMON_EXTERN void decode(Buffer& buffer); + QPID_COMMON_EXTERN uint32_t encodedSize() const; QPID_COMMON_EXTERN bool contains(const SequenceNumber& s) const; QPID_COMMON_EXTERN void add(const SequenceNumber& s); diff --git a/cpp/include/qpid/types/Variant.h b/cpp/include/qpid/types/Variant.h index 3feba4a0ec..3493559777 100644 --- a/cpp/include/qpid/types/Variant.h +++ b/cpp/include/qpid/types/Variant.h @@ -60,9 +60,9 @@ enum VariantType { VAR_UUID }; -std::string getTypeName(VariantType type); +QPID_TYPES_EXTERN std::string getTypeName(VariantType type); -bool isIntegerType(VariantType type); +QPID_TYPES_EXTERN bool isIntegerType(VariantType type); class VariantImpl; diff --git a/cpp/managementgen/qmfgen/management-types.xml b/cpp/managementgen/qmfgen/management-types.xml index c88f0caeae..efa8322806 100644 --- a/cpp/managementgen/qmfgen/management-types.xml +++ b/cpp/managementgen/qmfgen/management-types.xml @@ -35,7 +35,7 @@ <type name="int16" base="S16" cpp="int16_t" encode="@.putInt16(#)" decode="# = @.getInt16()" stream="#" size="2" accessor="direct" init="0"/> <type name="int32" base="S32" cpp="int32_t" encode="@.putInt32(#)" decode="# = @.getInt32()" stream="#" size="4" accessor="direct" init="0"/> <type name="int64" base="S64" cpp="int64_t" encode="@.putInt64(#)" decode="# = @.getInt64()" stream="#" size="8" accessor="direct" init="0"/> -<type name="bool" base="BOOL" cpp="bool" encode="@.putOctet(#?1:0)" decode="# = @.getOctet()==1" stream="#" size="1" accessor="direct" init="0"/> +<type name="bool" base="BOOL" cpp="bool" encode="@.putOctet(#?1:0)" decode="# = @.getOctet()==1" stream="#" size="1" accessor="direct" init="false"/> <type name="sstr" base="SSTR" cpp="std::string" encode="@.putShortString(#)" decode="@.getShortString(#)" stream="#" size="(1 + #.length())" accessor="direct" init='""' byRef="y" unmap="(#).getString()"/> <type name="lstr" base="LSTR" cpp="std::string" encode="@.putMediumString(#)" decode="@.getMediumString(#)" stream="#" size="(2 + #.length())" accessor="direct" init='""' byRef="y" unmap="(#).getString()"/> <type name="absTime" base="ABSTIME" cpp="int64_t" encode="@.putLongLong(#)" decode="# = @.getLongLong()" stream="#" size="8" accessor="direct" init="0"/> diff --git a/cpp/managementgen/qmfgen/schema.py b/cpp/managementgen/qmfgen/schema.py index b8a1d26fb0..dc8ffae446 100755 --- a/cpp/managementgen/qmfgen/schema.py +++ b/cpp/managementgen/qmfgen/schema.py @@ -225,8 +225,7 @@ class SchemaType: def genRead (self, stream, varName, indent=" "): stream.write(indent + self.decode.replace("@", "buf").replace("#", varName) + ";\n") - def genUnmap (self, stream, varName, indent=" ", key=None, mapName="_map", - _optional=False): + def genUnmap (self, stream, varName, indent=" ", key=None, mapName="_map", _optional=False, _default=None): if key is None: key = varName stream.write(indent + "if ((_i = " + mapName + ".find(\"" + key + "\")) != " + mapName + ".end()) {\n") @@ -234,6 +233,11 @@ class SchemaType: self.unmap.replace("#", "_i->second") + ";\n") if _optional: stream.write(indent + " _found = true;\n") + stream.write(indent + "} else {\n") + default = _default + if not default: + default = self.init + stream.write(indent + " " + varName + " = " + default + ";\n") stream.write(indent + "}\n") def genWrite (self, stream, varName, indent=" "): @@ -1405,7 +1409,9 @@ class SchemaClass: "ioArgs." + arg.dir.lower () + "_" + arg.name, " ", arg.name, - "inMap") + "inMap", + False, + arg.default) stream.write (" bool allow = coreObject->AuthorizeMethod(METHOD_" +\ method.getName().upper() + ", ioArgs, userId);\n") @@ -1471,7 +1477,7 @@ class SchemaClass: def genMethodIdDeclarations (self, stream, variables): number = 1 for method in self.methods: - stream.write (" static const uint32_t METHOD_" + method.getName().upper() +\ + stream.write (" QPID_BROKER_EXTERN static const uint32_t METHOD_" + method.getName().upper() +\ " = %d;\n" % number) number = number + 1 diff --git a/cpp/managementgen/qmfgen/templates/Class.h b/cpp/managementgen/qmfgen/templates/Class.h index 90f1b4dd4a..95939f3d03 100644 --- a/cpp/managementgen/qmfgen/templates/Class.h +++ b/cpp/managementgen/qmfgen/templates/Class.h @@ -24,6 +24,7 @@ /*MGEN:Root.Disclaimer*/ #include "qpid/management/ManagementObject.h" +#include "qmf/BrokerImportExport.h" namespace qpid { namespace management { @@ -34,7 +35,7 @@ namespace qpid { namespace qmf { /*MGEN:Class.OpenNamespaces*/ -class /*MGEN:Class.NameCap*/ : public ::qpid::management::ManagementObject +QPID_BROKER_CLASS_EXTERN class /*MGEN:Class.NameCap*/ : public ::qpid::management::ManagementObject { private: @@ -52,9 +53,12 @@ class /*MGEN:Class.NameCap*/ : public ::qpid::management::ManagementObject /*MGEN:Class.InstDeclarations*/ /*MGEN:IF(Class.ExistPerThreadStats)*/ // Per-Thread Statistics + + public: struct PerThreadStats { /*MGEN:Class.PerThreadDeclarations*/ }; + private: struct PerThreadStats** perThreadStatsArray; @@ -72,25 +76,25 @@ class /*MGEN:Class.NameCap*/ : public ::qpid::management::ManagementObject void aggregatePerThreadStats(struct PerThreadStats*) const; /*MGEN:ENDIF*/ public: - static void writeSchema(std::string& schema); - void mapEncodeValues(::qpid::types::Variant::Map& map, - bool includeProperties=true, - bool includeStatistics=true); - void mapDecodeValues(const ::qpid::types::Variant::Map& map); - void doMethod(std::string& methodName, - const ::qpid::types::Variant::Map& inMap, - ::qpid::types::Variant::Map& outMap, - const std::string& userId); - std::string getKey() const; + QPID_BROKER_EXTERN static void writeSchema(std::string& schema); + QPID_BROKER_EXTERN void mapEncodeValues(::qpid::types::Variant::Map& map, + bool includeProperties=true, + bool includeStatistics=true); + QPID_BROKER_EXTERN void mapDecodeValues(const ::qpid::types::Variant::Map& map); + QPID_BROKER_EXTERN void doMethod(std::string& methodName, + const ::qpid::types::Variant::Map& inMap, + ::qpid::types::Variant::Map& outMap, + const std::string& userId); + QPID_BROKER_EXTERN std::string getKey() const; /*MGEN:IF(Root.GenQMFv1)*/ - uint32_t writePropertiesSize() const; - void readProperties(const std::string& buf); - void writeProperties(std::string& buf) const; - void writeStatistics(std::string& buf, bool skipHeaders = false); - void doMethod(std::string& methodName, - const std::string& inBuf, - std::string& outBuf, - const std::string& userId); + QPID_BROKER_EXTERN uint32_t writePropertiesSize() const; + QPID_BROKER_EXTERN void readProperties(const std::string& buf); + QPID_BROKER_EXTERN void writeProperties(std::string& buf) const; + QPID_BROKER_EXTERN void writeStatistics(std::string& buf, bool skipHeaders = false); + QPID_BROKER_EXTERN void doMethod(std::string& methodName, + const std::string& inBuf, + std::string& outBuf, + const std::string& userId); /*MGEN:ENDIF*/ writeSchemaCall_t getWriteSchemaCall() { return writeSchema; } @@ -100,13 +104,17 @@ class /*MGEN:Class.NameCap*/ : public ::qpid::management::ManagementObject bool hasInst() { return false; } /*MGEN:ENDIF*/ - /*MGEN:Class.NameCap*/(::qpid::management::ManagementAgent* agent, - ::qpid::management::Manageable* coreObject/*MGEN:Class.ParentArg*//*MGEN:Class.ConstructorArgs*/); - ~/*MGEN:Class.NameCap*/(); + QPID_BROKER_EXTERN /*MGEN:Class.NameCap*/( + ::qpid::management::ManagementAgent* agent, + ::qpid::management::Manageable* coreObject/*MGEN:Class.ParentArg*//*MGEN:Class.ConstructorArgs*/); + + QPID_BROKER_EXTERN ~/*MGEN:Class.NameCap*/(); /*MGEN:Class.SetGeneralReferenceDeclaration*/ - static void registerSelf(::qpid::management::ManagementAgent* agent); + QPID_BROKER_EXTERN static void registerSelf( + ::qpid::management::ManagementAgent* agent); + std::string& getPackageName() const { return packageName; } std::string& getClassName() const { return className; } uint8_t* getMd5Sum() const { return md5Sum; } @@ -115,6 +123,11 @@ class /*MGEN:Class.NameCap*/ : public ::qpid::management::ManagementObject /*MGEN:Class.MethodIdDeclarations*/ // Accessor Methods /*MGEN:Class.AccessorMethods*/ + +/*MGEN:IF(Class.ExistPerThreadStats)*/ + struct PerThreadStats* getStatistics() { return getThreadStats(); } + void statisticsUpdated() { instChanged = true; } +/*MGEN:ENDIF*/ }; }/*MGEN:Class.CloseNamespaces*/ diff --git a/cpp/managementgen/qmfgen/templates/Event.h b/cpp/managementgen/qmfgen/templates/Event.h index 5fa5f8e576..592ae08c73 100644 --- a/cpp/managementgen/qmfgen/templates/Event.h +++ b/cpp/managementgen/qmfgen/templates/Event.h @@ -24,11 +24,12 @@ /*MGEN:Root.Disclaimer*/ #include "qpid/management/ManagementEvent.h" +#include "qmf/BrokerImportExport.h" namespace qmf { /*MGEN:Event.OpenNamespaces*/ -class Event/*MGEN:Event.NameCap*/ : public ::qpid::management::ManagementEvent +QPID_BROKER_CLASS_EXTERN class Event/*MGEN:Event.NameCap*/ : public ::qpid::management::ManagementEvent { private: static void writeSchema (std::string& schema); @@ -41,18 +42,18 @@ class Event/*MGEN:Event.NameCap*/ : public ::qpid::management::ManagementEvent public: writeSchemaCall_t getWriteSchemaCall(void) { return writeSchema; } - Event/*MGEN:Event.NameCap*/(/*MGEN:Event.ConstructorArgs*/); - ~Event/*MGEN:Event.NameCap*/() {}; + QPID_BROKER_EXTERN Event/*MGEN:Event.NameCap*/(/*MGEN:Event.ConstructorArgs*/); + QPID_BROKER_EXTERN ~Event/*MGEN:Event.NameCap*/() {}; static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getEventName() const { return eventName; } uint8_t* getMd5Sum() const { return md5Sum; } uint8_t getSeverity() const { return /*MGEN:Event.Severity*/; } - void encode(std::string& buffer) const; - void mapEncode(::qpid::types::Variant::Map& map) const; + QPID_BROKER_EXTERN void encode(std::string& buffer) const; + QPID_BROKER_EXTERN void mapEncode(::qpid::types::Variant::Map& map) const; - static bool match(const std::string& evt, const std::string& pkg); + QPID_BROKER_EXTERN static bool match(const std::string& evt, const std::string& pkg); }; }/*MGEN:Event.CloseNamespaces*/ diff --git a/cpp/managementgen/qmfgen/templates/Package.h b/cpp/managementgen/qmfgen/templates/Package.h index 569c7cfb33..3a42f12f9d 100644 --- a/cpp/managementgen/qmfgen/templates/Package.h +++ b/cpp/managementgen/qmfgen/templates/Package.h @@ -24,6 +24,7 @@ /*MGEN:Root.Disclaimer*/ #include "qpid//*MGEN:Class.AgentHeaderLocation*//ManagementAgent.h" +#include "qmf/BrokerImportExport.h" namespace qmf { /*MGEN:Class.OpenNamespaces*/ @@ -31,8 +32,8 @@ namespace qmf { class Package { public: - Package (::qpid::management::ManagementAgent* agent); - ~Package () {} + QPID_BROKER_EXTERN Package (::qpid::management::ManagementAgent* agent); + QPID_BROKER_EXTERN ~Package () {} }; }/*MGEN:Class.CloseNamespaces*/ diff --git a/cpp/rubygen/framing.0-10/structs.rb b/cpp/rubygen/framing.0-10/structs.rb index 62b33ce773..f93742e57e 100755 --- a/cpp/rubygen/framing.0-10/structs.rb +++ b/cpp/rubygen/framing.0-10/structs.rb @@ -185,6 +185,13 @@ class StructGen < CppGen end end + def check_field(f) + if (size = StringSizeMap[f.cpptype.encoded] and size < 4) + limit = 2 ** (size * 8) + genl "if (#{f.cppname}.size() >= #{limit}) throw IllegalArgumentException(\"Value for #{f.cppname} is too large\");" + end + end + def process_packed_fields(s) s.fields.each { |f| yield f, s.fields.index(f) } end @@ -260,11 +267,16 @@ EOS indent { process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.type_ == "bit"} process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.type_ == "bit"} + s.fields.each { |f| check_field(f) } } genl "}" else indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") } - genl "{}" + genl "{" + indent { + s.fields.each { |f| check_field(f) } + } + genl "}" end end #default constructors: @@ -298,6 +310,7 @@ EOS indent { genl "#{f.cppname} = _#{f.cppname};" genl "flags |= #{flag_mask(s, i)};" + check_field(f) } genl "}" genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }" @@ -329,6 +342,7 @@ EOS indent { genl "#{f.cppname} = _#{f.cppname};" genl "flags |= #{flag_mask(s, i)};" + check_field(f) } genl "}" genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }" @@ -364,7 +378,12 @@ EOS end def define_accessors(f) - genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) { #{f.cppname} = _#{f.cppname}; }" + genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "#{f.cppname} = _#{f.cppname};" + check_field(f) + } + genl "}" genl "#{f.cpptype.ret} get#{f.name.caps}() const { return #{f.cppname}; }" if (f.cpptype.name == "FieldTable") genl "#{f.cpptype.name}& get#{f.name.caps}() { return #{f.cppname}; }" @@ -401,6 +420,7 @@ EOS #include <ostream> #include "qpid/framing/amqp_types_full.h" +#include "qpid/framing/reply_exceptions.h" #include "qpid/CommonImportExport.h" namespace qpid { @@ -465,7 +485,7 @@ EOS end gen <<EOS #include "qpid/framing/#{classname}.h" -#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/Buffer.h" using namespace qpid::framing; diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 805e1bb9af..ecca265e08 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -354,6 +354,7 @@ endif (NOT Boost_REGEX_LIBRARY) # Boost on Windows can use automatic linking to pick up the correct # Boost libs based on compile-time touching of the headers. Since we don't # really need to add them to the link lines, set the names to blanks. +option(QPID_LINK_BOOST_DYNAMIC "Link with dynamic Boost libs (OFF to link static)" ON) if (MSVC) install (PROGRAMS ${Boost_DATE_TIME_LIBRARY_DEBUG} ${Boost_DATE_TIME_LIBRARY_RELEASE} @@ -371,7 +372,6 @@ if (MSVC) COMPONENT ${QPID_COMPONENT_COMMON}) endif (NOT Boost_VERSION LESS 103500) - option(QPID_LINK_BOOST_DYNAMIC "Link with dynamic Boost libs (OFF to link static)" ON) if (QPID_LINK_BOOST_DYNAMIC) add_definitions( /D BOOST_ALL_DYN_LINK) string (REPLACE .lib .dll @@ -477,6 +477,29 @@ else (NOT CLOCK_GETTIME_IN_RT) set(QPID_HAS_CLOCK_GETTIME YES CACHE BOOL "Platform has clock_gettime") endif (NOT CLOCK_GETTIME_IN_RT) +# Check for header file for dtrace static probes +check_include_files(sys/sdt.h HAVE_SYS_SDT_H) +if (HAVE_SYS_SDT_H) + set(probes_default ON) +endif (HAVE_SYS_SDT_H) +option(BUILD_PROBES "Build with DTrace/systemtap static probes" ${probes_default}) +if (NOT BUILD_PROBES) + set (HAVE_SYS_SDT_H 0) +endif (NOT BUILD_PROBES) + +# Check for poll/epoll header files +check_include_files(sys/poll.h HAVE_POLL) +check_include_files(sys/epoll.h HAVE_EPOLL) + +# Set default poller implementation (check from general to specific to allow overriding) +if (HAVE_POLL) + set(poller_default poll) +endif (HAVE_POLL) +if (HAVE_EPOLL) + set(poller_default epoll) +endif (HAVE_EPOLL) +set(POLLER ${poller_default} CACHE STRING "Poller implementation (poll/epoll)") + # If not windows ensure that we have uuid library if (NOT CMAKE_SYSTEM_NAME STREQUAL Windows) CHECK_LIBRARY_EXISTS (uuid uuid_compare "" HAVE_UUID) @@ -571,6 +594,8 @@ if (BUILD_ACL) set (acl_SOURCES qpid/acl/Acl.cpp qpid/acl/Acl.h + qpid/acl/AclConnectionCounter.cpp + qpid/acl/AclConnectionCounter.h qpid/acl/AclData.cpp qpid/acl/AclData.h qpid/acl/AclPlugin.cpp @@ -609,6 +634,8 @@ if (BUILD_HA) qpid/ha/Settings.h qpid/ha/QueueReplicator.h qpid/ha/QueueReplicator.cpp + qpid/ha/ReplicateLevel.h + qpid/ha/ReplicateLevel.cpp qpid/ha/ReplicatingSubscription.h qpid/ha/ReplicatingSubscription.cpp qpid/ha/BrokerReplicator.cpp @@ -619,7 +646,7 @@ if (BUILD_HA) add_library (ha MODULE ${ha_SOURCES}) set_target_properties (ha PROPERTIES PREFIX "") - target_link_libraries (ha qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY}) + target_link_libraries (ha qpidcommon qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY}) if (CMAKE_COMPILER_IS_GNUCXX) set_target_properties (ha PROPERTIES PREFIX "" @@ -740,9 +767,18 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows) # POSIX (Non-Windows) platforms have a lot of overlap in sources; the only # major difference is the poller module. - if (CMAKE_SYSTEM_NAME STREQUAL Linux) + if (POLLER STREQUAL poll) + set (qpid_poller_module + qpid/sys/posix/PosixPoller.cpp + ) + elseif (POLLER STREQUAL epoll) set (qpid_poller_module qpid/sys/epoll/EpollPoller.cpp + ) + endif (POLLER STREQUAL poll) + + if (CMAKE_SYSTEM_NAME STREQUAL Linux) + set (qpid_system_module qpid/sys/posix/SystemInfo.cpp ) add_definitions(-pthread) @@ -754,13 +790,12 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows) set (qpidtypes_platform_SOURCES) set (qpidtypes_platform_LIBS - uuid - ${Boost_SYSTEM_LIBRARY} + uuid + ${Boost_SYSTEM_LIBRARY} ) if (CMAKE_SYSTEM_NAME STREQUAL SunOS) - set (qpid_poller_module - qpid/sys/posix/PosixPoller.cpp + set (qpid_system_module qpid/sys/solaris/SystemInfo.cpp ) # On Sun we want -lpthread -lthread as the 2nd last and last libs passed to linker @@ -789,6 +824,7 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows) qpid/sys/posix/Time.cpp qpid/SaslFactory.cpp + ${qpid_system_module} ${qpid_poller_module} ) set (qpidcommon_platform_LIBS @@ -842,7 +878,6 @@ set (qpidcommon_SOURCES qpid/framing/AMQHeaderBody.cpp qpid/framing/AMQHeartbeatBody.cpp qpid/framing/Array.cpp - qpid/framing/BodyHandler.cpp qpid/framing/Buffer.cpp qpid/framing/Endian.cpp qpid/framing/FieldTable.cpp @@ -1082,6 +1117,7 @@ set (qpidbroker_SOURCES qpid/broker/NameGenerator.cpp qpid/broker/NullMessageStore.cpp qpid/broker/QueueBindings.cpp + qpid/broker/QueuedMessage.cpp qpid/broker/QueueEvents.cpp qpid/broker/QueuePolicy.cpp qpid/broker/QueueRegistry.cpp diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 7162245c53..43e491a229 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -102,15 +102,16 @@ $(srcdir)/rubygen.cmake: $(rgen_generator) $(specs) # Management generator. mgen_dir=$(top_srcdir)/managementgen -mgen_cmd=$(mgen_dir)/qmf-gen -m $(srcdir)/managementgen.mk \ - -c $(srcdir)/managementgen.cmake -q -b -o qmf \ - $(top_srcdir)/../specs/management-schema.xml \ +mgen_xml=$(top_srcdir)/../specs/management-schema.xml \ $(srcdir)/qpid/acl/management-schema.xml \ $(srcdir)/qpid/cluster/management-schema.xml \ $(srcdir)/qpid/ha/management-schema.xml +mgen_cmd=$(mgen_dir)/qmf-gen -m $(srcdir)/managementgen.mk \ + -c $(srcdir)/managementgen.cmake -q -b -o qmf \ + $(mgen_xml) $(srcdir)/managementgen.mk $(mgen_broker_cpp) $(dist_qpid_management_HEADERS): mgen.timestamp -mgen.timestamp: $(mgen_generator) +mgen.timestamp: $(mgen_generator) $(mgen_xml) $(mgen_cmd); touch $@ $(mgen_generator): @@ -139,7 +140,7 @@ tmoduleexec_LTLIBRARIES= AM_CXXFLAGS += -DBOOST_FILESYSTEM_VERSION=2 ## Automake macros to build libraries and executables. -qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\" +qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\" libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_MODULE_DIR=\"$(cmoduleexecdir)\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\" qpidd_LDADD = \ @@ -183,11 +184,15 @@ nobase_include_HEADERS += \ ../include/qpid/sys/posix/Time.h \ ../include/qpid/sys/posix/check.h -if HAVE_EPOLL +if USE_EPOLL poller = qpid/sys/epoll/EpollPoller.cpp endif -if HAVE_ECF +if USE_POLL + poller = qpid/sys/posix/PosixPoller.cpp +endif + +if USE_ECF poller = qpid/sys/solaris/ECFPoller.cpp endif @@ -393,8 +398,6 @@ libqpidcommon_la_SOURCES += \ qpid/framing/AccumulatedAck.h \ qpid/framing/Array.cpp \ qpid/framing/BodyFactory.h \ - qpid/framing/BodyHandler.cpp \ - qpid/framing/BodyHandler.h \ qpid/framing/Buffer.cpp \ qpid/framing/ResizableBuffer.h \ qpid/framing/ChannelHandler.h \ @@ -481,12 +484,14 @@ libqpidcommon_la_SOURCES += \ qpid/sys/Fork.h \ qpid/sys/LockFile.h \ qpid/sys/LockPtr.h \ + qpid/sys/MemStat.h \ qpid/sys/OutputControl.h \ qpid/sys/OutputTask.h \ qpid/sys/PipeHandle.h \ qpid/sys/PollableCondition.h \ qpid/sys/PollableQueue.h \ qpid/sys/Poller.h \ + qpid/sys/Probes.h \ qpid/sys/ProtocolFactory.h \ qpid/sys/Runnable.cpp \ qpid/sys/ScopedIncrement.h \ @@ -632,10 +637,10 @@ libqpidbroker_la_SOURCES = \ qpid/broker/QueuePolicy.h \ qpid/broker/QueueRegistry.cpp \ qpid/broker/QueueRegistry.h \ + qpid/broker/QueuedMessage.cpp \ qpid/broker/QueuedMessage.h \ qpid/broker/QueueFlowLimit.h \ qpid/broker/QueueFlowLimit.cpp \ - qpid/broker/RateFlowcontrol.h \ qpid/broker/RecoverableConfig.h \ qpid/broker/RecoverableExchange.h \ qpid/broker/RecoverableMessage.h \ diff --git a/cpp/src/acl.mk b/cpp/src/acl.mk index b8e2ff0e13..0301f8c754 100644 --- a/cpp/src/acl.mk +++ b/cpp/src/acl.mk @@ -24,6 +24,8 @@ dmoduleexec_LTLIBRARIES += acl.la acl_la_SOURCES = \ qpid/acl/Acl.cpp \ qpid/acl/Acl.h \ + qpid/acl/AclConnectionCounter.cpp \ + qpid/acl/AclConnectionCounter.h \ qpid/acl/AclData.cpp \ qpid/acl/AclData.h \ qpid/acl/AclPlugin.cpp \ diff --git a/cpp/src/config.h.cmake b/cpp/src/config.h.cmake index 2bb84c6e47..f55f68afde 100644 --- a/cpp/src/config.h.cmake +++ b/cpp/src/config.h.cmake @@ -60,6 +60,7 @@ #cmakedefine HAVE_OPENAIS_CPG_H ${HAVE_OPENAIS_CPG_H} #cmakedefine HAVE_COROSYNC_CPG_H ${HAVE_COROSYNC_CPG_H} #cmakedefine HAVE_LIBCMAN_H ${HAVE_LIBCMAN_H} +#cmakedefine HAVE_SYS_SDT_H ${HAVE_SYS_SDT_H} #cmakedefine HAVE_LOG_AUTHPRIV #cmakedefine HAVE_LOG_FTP diff --git a/cpp/src/ha.mk b/cpp/src/ha.mk index 8a2cee30c7..be1fb73e89 100644 --- a/cpp/src/ha.mk +++ b/cpp/src/ha.mk @@ -25,18 +25,20 @@ dmoduleexec_LTLIBRARIES += ha.la ha_la_SOURCES = \ qpid/ha/Backup.cpp \ qpid/ha/Backup.h \ + qpid/ha/BrokerReplicator.cpp \ + qpid/ha/BrokerReplicator.h \ + qpid/ha/ConnectionExcluder.cpp \ + qpid/ha/ConnectionExcluder.h \ qpid/ha/HaBroker.cpp \ qpid/ha/HaBroker.h \ qpid/ha/HaPlugin.cpp \ - qpid/ha/Settings.h \ - qpid/ha/QueueReplicator.h \ qpid/ha/QueueReplicator.cpp \ - qpid/ha/ReplicatingSubscription.h \ + qpid/ha/QueueReplicator.h \ + qpid/ha/ReplicateLevel.cpp \ + qpid/ha/ReplicateLevel.h \ qpid/ha/ReplicatingSubscription.cpp \ - qpid/ha/BrokerReplicator.cpp \ - qpid/ha/BrokerReplicator.h \ - qpid/ha/ConnectionExcluder.cpp \ - qpid/ha/ConnectionExcluder.h + qpid/ha/ReplicatingSubscription.h \ + qpid/ha/Settings.h ha_la_LIBADD = libqpidbroker.la ha_la_LDFLAGS = $(PLUGINLDFLAGS) diff --git a/cpp/src/posix/QpiddBroker.cpp b/cpp/src/posix/QpiddBroker.cpp index 1cebcfc3ac..fd2fb6184f 100644 --- a/cpp/src/posix/QpiddBroker.cpp +++ b/cpp/src/posix/QpiddBroker.cpp @@ -31,14 +31,15 @@ #include <unistd.h> #include <sys/utsname.h> -using namespace std; -using namespace qpid; -using qpid::broker::Broker; -using qpid::broker::Daemon; +using std::cout; +using std::endl; + +namespace qpid { +namespace broker { BootstrapOptions::BootstrapOptions(const char* argv0) : qpid::Options("Options"), - common("", QPIDD_CONF_FILE), + common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE), module(QPIDD_MODULE_DIR), log(argv0) { @@ -90,7 +91,7 @@ struct QpiddPosixOptions : public QpiddOptionsPrivate { QpiddOptions::QpiddOptions(const char* argv0) : qpid::Options("Options"), - common("", QPIDD_CONF_FILE), + common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE), module(QPIDD_MODULE_DIR), log(argv0) { @@ -197,7 +198,9 @@ int QpiddBroker::execute (QpiddOptions *options) { return 0; } +}} // namespace qpid::Broker + int main(int argc, char* argv[]) { - return run_broker(argc, argv); + return qpid::broker::run_broker(argc, argv); } diff --git a/cpp/src/qmf.mk b/cpp/src/qmf.mk index 95d3d5c9b0..9b5df6c808 100644 --- a/cpp/src/qmf.mk +++ b/cpp/src/qmf.mk @@ -96,6 +96,7 @@ libqmf2_la_SOURCES = \ qmf/AgentSessionImpl.h \ qmf/AgentSubscription.cpp \ qmf/AgentSubscription.h \ + qmf/BrokerImportExport.h \ qmf/ConsoleEvent.cpp \ qmf/ConsoleEventImpl.h \ qmf/ConsoleSession.cpp \ diff --git a/cpp/src/qpid/sys/apr/APRPool.cpp b/cpp/src/qmf/BrokerImportExport.h index e221bfc2f1..ee05788063 100644 --- a/cpp/src/qpid/sys/apr/APRPool.cpp +++ b/cpp/src/qmf/BrokerImportExport.h @@ -1,5 +1,7 @@ +#ifndef QPID_BROKER_IMPORT_EXPORT_H +#define QPID_BROKER_IMPORT_EXPORT_H + /* - * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -7,35 +9,34 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. - * */ -#include "qpid/sys/apr/APRPool.h" -#include "qpid/sys/apr/APRBase.h" -#include <boost/pool/detail/singleton.hpp> - -using namespace qpid::sys; - -APRPool::APRPool(){ - APRBase::increment(); - CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); -} - -APRPool::~APRPool(){ - apr_pool_destroy(pool); - APRBase::decrement(); -} - -apr_pool_t* APRPool::get() { - return boost::details::pool::singleton_default<APRPool>::instance().pool; -} +#if defined(WIN32) && !defined(QPID_DECLARE_STATIC) +# if defined(BROKER_EXPORT) || defined (qpidbroker_EXPORTS) +# define QPID_BROKER_EXTERN __declspec(dllexport) +# else +# define QPID_BROKER_EXTERN __declspec(dllimport) +# endif +# ifdef _MSC_VER +# define QPID_BROKER_CLASS_EXTERN +# define QPID_BROKER_INLINE_EXTERN QPID_BROKER_EXTERN +# else +# define QPID_BROKER_CLASS_EXTERN QPID_BROKER_EXTERN +# define QPID_BROKER_INLINE_EXTERN +# endif +#else +# define QPID_BROKER_EXTERN +# define QPID_BROKER_CLASS_EXTERN +# define QPID_BROKER_INLINE_EXTERN +#endif +#endif diff --git a/cpp/src/qpid/Options.cpp b/cpp/src/qpid/Options.cpp index 4b13e349f5..35787aa8f3 100644 --- a/cpp/src/qpid/Options.cpp +++ b/cpp/src/qpid/Options.cpp @@ -186,13 +186,14 @@ void Options::parse(int argc, char const* const* argv, const std::string& config } } -CommonOptions::CommonOptions(const string& name, const string& configfile) - : Options(name), config(configfile) +CommonOptions::CommonOptions(const string& name, const string& configfile, const string& clientfile) + : Options(name), config(configfile), clientConfig(clientfile) { addOptions() ("help,h", optValue(help), "Displays the help message") ("version,v", optValue(version), "Displays version information") - ("config", optValue(config, "FILE"), "Reads configuration from FILE"); + ("config", optValue(config, "FILE"), "Reads configuration from FILE") + ("client-config", optValue(clientConfig, "FILE"), "Reads client configuration from FILE (for cluster interconnect)"); } diff --git a/cpp/src/qpid/Url.cpp b/cpp/src/qpid/Url.cpp index 2061499ec3..840f46e928 100644 --- a/cpp/src/qpid/Url.cpp +++ b/cpp/src/qpid/Url.cpp @@ -111,7 +111,8 @@ ssl_addr = "ssl:" host [":" port] */ class UrlParser { public: - UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {} + UrlParser(Url& u, const char* s, const std::string& defaultProtocol_=Address::TCP) : url(u), text(s), end(s+strlen(s)), i(s), + defaultProtocol(defaultProtocol_) {} bool parse() { literal("amqp:"); // Optional userPass(); // Optional @@ -135,7 +136,7 @@ class UrlParser { bool comma() { return literal(","); } bool protocolAddr() { - Address addr(Address::TCP, "", Address::AMQP_PORT); // Set up defaults + Address addr(defaultProtocol, "", Address::AMQP_PORT); // Set up defaults protocolTag(addr.protocol); // Optional bool ok = (host(addr.host) && (literal(":") ? port(addr.port) : true)); @@ -244,20 +245,28 @@ class UrlParser { const char* text; const char* end; const char* i; + const std::string defaultProtocol; }; const string UrlParser::LOCALHOST("127.0.0.1"); void Url::parse(const char* url) { - parseNoThrow(url); + parse(url, Address::TCP); +} +void Url::parse(const char* url, const std::string& defaultProtocol) { + parseNoThrow(url, defaultProtocol); if (empty()) throw Url::Invalid(QPID_MSG("Invalid URL: " << url)); } void Url::parseNoThrow(const char* url) { + parseNoThrow(url, Address::TCP); +} + +void Url::parseNoThrow(const char* url, const std::string& defaultProtocol) { clear(); cache.clear(); - if (!UrlParser(*this, url).parse()) + if (!UrlParser(*this, url, defaultProtocol).parse()) clear(); } diff --git a/cpp/src/qpid/UrlArray.cpp b/cpp/src/qpid/UrlArray.cpp index 489309c8ad..9ebacbd945 100644 --- a/cpp/src/qpid/UrlArray.cpp +++ b/cpp/src/qpid/UrlArray.cpp @@ -20,6 +20,8 @@ */ #include "UrlArray.h" +#include <qpid/framing/FieldValue.h> + namespace qpid { std::vector<Url> urlArrayToVector(const framing::Array& array) { diff --git a/cpp/src/qpid/UrlArray.h b/cpp/src/qpid/UrlArray.h index 8b11df5c73..ce9e42f248 100644 --- a/cpp/src/qpid/UrlArray.h +++ b/cpp/src/qpid/UrlArray.h @@ -23,7 +23,6 @@ */ #include "qpid/framing/Array.h" -#include "qpid/framing/FieldValue.h" #include "qpid/Url.h" #include <vector> diff --git a/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp index 12bf13018c..917c2e3398 100644 --- a/cpp/src/qpid/acl/Acl.cpp +++ b/cpp/src/qpid/acl/Acl.cpp @@ -17,6 +17,7 @@ */ #include "qpid/acl/Acl.h" +#include "qpid/acl/AclConnectionCounter.h" #include "qpid/acl/AclData.h" #include "qpid/acl/AclValidator.h" #include "qpid/sys/Mutex.h" @@ -26,8 +27,11 @@ #include "qpid/Options.h" #include "qpid/log/Logger.h" #include "qpid/types/Variant.h" +#include "qmf/org/apache/qpid/acl/ArgsAclLookup.h" +#include "qmf/org/apache/qpid/acl/ArgsAclLookupPublish.h" #include "qmf/org/apache/qpid/acl/Package.h" #include "qmf/org/apache/qpid/acl/EventAllow.h" +#include "qmf/org/apache/qpid/acl/EventConnectionDeny.h" #include "qmf/org/apache/qpid/acl/EventDeny.h" #include "qmf/org/apache/qpid/acl/EventFileLoaded.h" #include "qmf/org/apache/qpid/acl/EventFileLoadFailed.h" @@ -35,7 +39,6 @@ #include <map> #include <boost/shared_ptr.hpp> -#include <boost/utility/in_place_factory.hpp> using namespace std; using namespace qpid::acl; @@ -47,7 +50,8 @@ using qpid::management::Manageable; using qpid::management::Args; namespace _qmf = qmf::org::apache::qpid::acl; -Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0) +Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0), + connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp)) { agent = broker->getManagementAgent(); @@ -62,14 +66,30 @@ Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(fals throw Exception("Could not read ACL file " + errorString); if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0); } + broker->getConnectionObservers().add(connectionCounter); QPID_LOG(info, "ACL Plugin loaded"); if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1); } -bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params) + +void Acl::reportConnectLimit(const std::string user, const std::string addr) +{ + if (mgmtObject!=0) + mgmtObject->inc_connectionDenyCount(); + + agent->raiseEvent(_qmf::EventConnectionDeny(user, addr)); +} + + +bool Acl::authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name, + std::map<Property, std::string>* params) { boost::shared_ptr<AclData> dataLocal; - { + { Mutex::ScopedLock locker(dataLock); dataLocal = data; //rcu copy } @@ -81,7 +101,12 @@ bool Acl::authorise(const std::string& id, const Action& action, const ObjectTyp return result(aclreslt, id, action, objType, name); } -bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey) +bool Acl::authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey) { boost::shared_ptr<AclData> dataLocal; { @@ -96,31 +121,50 @@ bool Acl::authorise(const std::string& id, const Action& action, const ObjectTyp } -bool Acl::result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name) +bool Acl::result( + const AclResult& aclreslt, + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name) { + bool result(false); + switch (aclreslt) { case ALLOWLOG: - QPID_LOG(info, "ACL Allow id:" << id <<" action:" << AclHelper::getActionStr(action) << - " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name ); + QPID_LOG(info, "ACL Allow id:" << id + << " action:" << AclHelper::getActionStr(action) + << " ObjectType:" << AclHelper::getObjectTypeStr(objType) + << " Name:" << name ); agent->raiseEvent(_qmf::EventAllow(id, AclHelper::getActionStr(action), - AclHelper::getObjectTypeStr(objType), - name, types::Variant::Map())); + AclHelper::getObjectTypeStr(objType), + name, types::Variant::Map())); + // FALLTHROUGH case ALLOW: - return true; - case DENY: - if (mgmtObject!=0) mgmtObject->inc_aclDenyCount(); - return false; + result = true; + break; + case DENYLOG: - if (mgmtObject!=0) mgmtObject->inc_aclDenyCount(); - default: - QPID_LOG(info, "ACL Deny id:" << id << " action:" << AclHelper::getActionStr(action) << " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name); + QPID_LOG(info, "ACL Deny id:" << id + << " action:" << AclHelper::getActionStr(action) + << " ObjectType:" << AclHelper::getObjectTypeStr(objType) + << " Name:" << name); agent->raiseEvent(_qmf::EventDeny(id, AclHelper::getActionStr(action), - AclHelper::getObjectTypeStr(objType), - name, types::Variant::Map())); - return false; + AclHelper::getObjectTypeStr(objType), + name, types::Variant::Map())); + // FALLTHROUGH + case DENY: + if (mgmtObject!=0) + mgmtObject->inc_aclDenyCount(); + result = false; + break; + + default: + assert (false); } - return false; + + return result; } bool Acl::readAclFile(std::string& errorText) @@ -129,7 +173,7 @@ bool Acl::readAclFile(std::string& errorText) return readAclFile(aclValues.aclFile, errorText); } -bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { +bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { boost::shared_ptr<AclData> d(new AclData); AclReader ar; if (ar.read(aclFile, d)){ @@ -142,17 +186,17 @@ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { AclValidator validator; validator.validate(d); - { + { Mutex::ScopedLock locker(dataLock); data = d; } transferAcl = data->transferAcl; // any transfer ACL if (data->transferAcl){ - QPID_LOG(debug,"Transfer ACL is Enabled!"); + QPID_LOG(debug,"ACL: Transfer ACL is Enabled!"); } - data->aclSource = aclFile; + data->aclSource = aclFile; if (mgmtObject!=0){ mgmtObject->set_transferAcl(transferAcl?1:0); mgmtObject->set_policyFile(aclFile); @@ -164,17 +208,92 @@ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { return true; } -Acl::~Acl(){} + +// +// management lookup function performs general query on acl engine +// +Manageable::status_t Acl::lookup(qpid::management::Args& args, std::string& text) +{ + _qmf::ArgsAclLookup& ioArgs = (_qmf::ArgsAclLookup&) args; + Manageable::status_t result(STATUS_USER); + + try { + ObjectType objType = AclHelper::getObjectType(ioArgs.i_object); + Action action = AclHelper::getAction( ioArgs.i_action); + std::map<Property, std::string> propertyMap; + for (::qpid::types::Variant::Map::const_iterator + iMapIter = ioArgs.i_propertyMap.begin(); + iMapIter != ioArgs.i_propertyMap.end(); + iMapIter++) + { + Property property = AclHelper::getProperty(iMapIter->first); + propertyMap.insert(make_pair(property, iMapIter->second)); + } + + boost::shared_ptr<AclData> dataLocal; + { + Mutex::ScopedLock locker(dataLock); + dataLocal = data; //rcu copy + } + AclResult aclResult = dataLocal->lookup( + ioArgs.i_userId, + action, + objType, + ioArgs.i_objectName, + &propertyMap); + + ioArgs.o_result = AclHelper::getAclResultStr(aclResult); + result = STATUS_OK; + + } catch (const std::exception& e) { + std::ostringstream oss; + oss << "AclLookup invalid name : " << e.what(); + ioArgs.o_result = oss.str(); + text = oss.str(); + } + + return result; +} + + +// +// management lookupPublish function performs fastpath +// PUBLISH EXCHANGE query on acl engine +// +Manageable::status_t Acl::lookupPublish(qpid::management::Args& args, std::string& /*text*/) +{ + _qmf::ArgsAclLookupPublish& ioArgs = (_qmf::ArgsAclLookupPublish&) args; + boost::shared_ptr<AclData> dataLocal; + { + Mutex::ScopedLock locker(dataLock); + dataLocal = data; //rcu copy + } + AclResult aclResult = dataLocal->lookup( + ioArgs.i_userId, + ACT_PUBLISH, + OBJ_EXCHANGE, + ioArgs.i_exchangeName, + ioArgs.i_routingKey); + + ioArgs.o_result = AclHelper::getAclResultStr(aclResult); + + return STATUS_OK; +} + + +Acl::~Acl(){ + broker->getConnectionObservers().remove(connectionCounter); +} ManagementObject* Acl::GetManagementObject(void) const { return (ManagementObject*) mgmtObject; } -Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, string& text) +Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& args, string& text) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; - QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]"); + QPID_LOG (debug, "ACL: Queue::ManagementMethod [id=" << methodId << "]"); switch (methodId) { @@ -185,6 +304,14 @@ Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, s else status = Manageable::STATUS_USER; break; + + case _qmf::Acl::METHOD_LOOKUP : + status = lookup(args, text); + break; + + case _qmf::Acl::METHOD_LOOKUPPUBLISH : + status = lookupPublish(args, text); + break; } return status; diff --git a/cpp/src/qpid/acl/Acl.h b/cpp/src/qpid/acl/Acl.h index 77f43838de..c3451018ef 100644 --- a/cpp/src/qpid/acl/Acl.h +++ b/cpp/src/qpid/acl/Acl.h @@ -30,6 +30,7 @@ #include "qmf/org/apache/qpid/acl/Acl.h" #include "qpid/sys/Mutex.h" +#include <boost/shared_ptr.hpp> #include <map> #include <string> @@ -40,9 +41,12 @@ class Broker; } namespace acl { +class ConnectionCounter; struct AclValues { - std::string aclFile; + std::string aclFile; + uint32_t aclMaxConnectPerUser; + uint32_t aclMaxConnectPerIp; }; @@ -50,37 +54,56 @@ class Acl : public broker::AclModule, public RefCounted, public management::Mana { private: - acl::AclValues aclValues; - broker::Broker* broker; - bool transferAcl; - boost::shared_ptr<AclData> data; - qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle - qpid::management::ManagementAgent* agent; - mutable qpid::sys::Mutex dataLock; + acl::AclValues aclValues; + broker::Broker* broker; + bool transferAcl; + boost::shared_ptr<AclData> data; + qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle + qpid::management::ManagementAgent* agent; + mutable qpid::sys::Mutex dataLock; + boost::shared_ptr<ConnectionCounter> connectionCounter; public: - Acl (AclValues& av, broker::Broker& b); - - void initialize(); - - inline virtual bool doTransferAcl() {return transferAcl;}; - - // create specilied authorise methods for cases that need faster matching as needed. - virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0); - virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName,const std::string& RoutingKey); - - virtual ~Acl(); + Acl (AclValues& av, broker::Broker& b); + + void reportConnectLimit(const std::string user, const std::string addr); + + inline virtual bool doTransferAcl() { + return transferAcl; + }; + +// create specilied authorise methods for cases that need faster matching as needed. + virtual bool authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name, + std::map<Property, std::string>* params=0); + + virtual bool authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey); + + virtual ~Acl(); private: - bool result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name); - bool readAclFile(std::string& errorText); - bool readAclFile(std::string& aclFile, std::string& errorText); - virtual qpid::management::ManagementObject* GetManagementObject(void) const; - virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + bool result( + const AclResult& aclreslt, + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name); + bool readAclFile(std::string& errorText); + bool readAclFile(std::string& aclFile, std::string& errorText); + Manageable::status_t lookup (management::Args& args, std::string& text); + Manageable::status_t lookupPublish(management::Args& args, std::string& text); + virtual qpid::management::ManagementObject* GetManagementObject(void) const; + virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); }; - - }} // namespace qpid::acl #endif // QPID_ACL_ACL_H diff --git a/cpp/src/qpid/acl/AclConnectionCounter.cpp b/cpp/src/qpid/acl/AclConnectionCounter.cpp new file mode 100644 index 0000000000..5d4e3c1544 --- /dev/null +++ b/cpp/src/qpid/acl/AclConnectionCounter.cpp @@ -0,0 +1,214 @@ +/* + * + * 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 "AclConnectionCounter.h" +#include "Acl.h" +#include "qpid/broker/Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Mutex.h" +#include <assert.h> +#include <sstream> + +using namespace qpid::sys; + +namespace qpid { +namespace acl { + +// +// This module instantiates a broker::ConnectionObserver and limits client +// connections by counting connections per user name and per client IP address. +// + + +// +// +// +ConnectionCounter::ConnectionCounter(Acl& a, uint32_t nl, uint32_t hl) : + acl(a), nameLimit(nl), hostLimit(hl) {} + +ConnectionCounter::~ConnectionCounter() {} + + +// +// limitCheckLH +// +// Increment the name's count in map and return a comparison against the limit. +// called with dataLock already taken +// +bool ConnectionCounter::limitCheckLH( + connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) { + + bool result(true); + if (theLimit > 0) { + connectCountsMap_t::iterator eRef = theMap.find(theName); + if (eRef != theMap.end()) { + uint32_t count = (uint32_t)(*eRef).second + 1; + (*eRef).second = count; + result = count <= theLimit; + } else { + theMap[theName] = 1; + } + } + return result; +} + + +// +// releaseLH +// +// Decrement the name's count in map. +// called with dataLock already taken +// +void ConnectionCounter::releaseLH( + connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) { + + if (theLimit > 0) { + connectCountsMap_t::iterator eRef = theMap.find(theName); + if (eRef != theMap.end()) { + uint32_t count = (uint32_t) (*eRef).second; + assert (count > 0); + if (1 == count) { + theMap.erase (eRef); + } else { + (*eRef).second = count - 1; + } + } else { + // User had no connections. + QPID_LOG(notice, "ACL ConnectionCounter Connection for '" << theName + << "' not found in connection count pool"); + } + } +} + + +// +// connection - called during Connection's constructor +// +void ConnectionCounter::connection(broker::Connection& connection) { + QPID_LOG(trace, "ACL ConnectionCounter connection IP:" << connection.getMgmtId() + << ", userId:" << connection.getUserId()); + + Mutex::ScopedLock locker(dataLock); + + connectProgressMap[connection.getMgmtId()] = C_CREATED; +} + + +// +// opened - called when first AMQP frame is received over Connection +// +void ConnectionCounter::opened(broker::Connection& connection) { + QPID_LOG(trace, "ACL ConnectionCounter Opened IP:" << connection.getMgmtId() + << ", userId:" << connection.getUserId()); + + Mutex::ScopedLock locker(dataLock); + + const std::string& userName( connection.getUserId()); + const std::string& hostName(getClientHost(connection.getMgmtId())); + + // Bump state from CREATED to OPENED + (void) limitCheckLH(connectProgressMap, connection.getMgmtId(), C_OPENED); + + bool nameOk = limitCheckLH(connectByNameMap, userName, nameLimit); + bool hostOk = limitCheckLH(connectByHostMap, hostName, hostLimit); + + if (!nameOk) { + // User has too many + acl.reportConnectLimit(userName, hostName); + QPID_LOG(notice, "ACL ConnectionCounter User '" << userName + << "' exceeded maximum allowed connections"); + throw Exception( + QPID_MSG("User '" << userName + << "' exceeded maximum allowed connections")); + } + + if (!hostOk) { + // Host has too many + acl.reportConnectLimit(userName, hostName); + QPID_LOG(notice, "ACL ConnectionCounter Client host '" << hostName + << "' exceeded maximum allowed connections"); + throw Exception( + QPID_MSG("Client host '" << hostName + << "' exceeded maximum allowed connections")); + } +} + + +// +// closed - called during Connection's destructor +// +void ConnectionCounter::closed(broker::Connection& connection) { + QPID_LOG(trace, "ACL ConnectionCounter Closed IP:" << connection.getMgmtId() + << ", userId:" << connection.getUserId()); + + Mutex::ScopedLock locker(dataLock); + + connectCountsMap_t::iterator eRef = connectProgressMap.find(connection.getMgmtId()); + if (eRef != connectProgressMap.end()) { + if ((*eRef).second == C_OPENED){ + // Normal case: connection was created and opened. + // Decrement in-use counts + releaseLH(connectByNameMap, + connection.getUserId(), + nameLimit); + + releaseLH(connectByHostMap, + getClientHost(connection.getMgmtId()), + hostLimit); + } else { + // Connection was created but not opened. + // Don't decrement any connection counts. + } + connectProgressMap.erase(eRef); + + } else { + // connection not found in progress map + QPID_LOG(notice, "ACL ConnectionCounter info for '" << connection.getMgmtId() + << "' not found in connection state pool"); + } +} + + +// +// getClientIp - given a connection's mgmtId return the client host part. +// +// TODO: Ideally this would be a method of the connection itself. +// +std::string ConnectionCounter::getClientHost(const std::string mgmtId) +{ + size_t hyphen = mgmtId.find('-'); + if (std::string::npos != hyphen) { + size_t colon = mgmtId.find_last_of(':'); + if (std::string::npos != colon) { + // trailing colon found + return mgmtId.substr(hyphen+1, colon - hyphen - 1); + } else { + // colon not found - use everything after hyphen + return mgmtId.substr(hyphen+1); + } + } + + // no hyphen found - use whole string + assert(false); + return mgmtId; +} + +}} // namespace qpid::ha diff --git a/cpp/src/qpid/acl/AclConnectionCounter.h b/cpp/src/qpid/acl/AclConnectionCounter.h new file mode 100644 index 0000000000..31d11540fd --- /dev/null +++ b/cpp/src/qpid/acl/AclConnectionCounter.h @@ -0,0 +1,81 @@ +#ifndef QPID_ACL_CONNECTIONCOUNTER_H +#define QPID_ACL_CONNECTIONCOUNTER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/ConnectionObserver.h" +#include "qpid/sys/Mutex.h" +#include <boost/iterator/iterator_concepts.hpp> + +#include <map> + +namespace qpid { + +namespace broker { +class Connection; +} + +namespace acl { +class Acl; + + /** + * Terminate client connections when a user tries to create 'too many'. + * Terminate hostIp connections when an IP host tries to create 'too many'. + */ +class ConnectionCounter : public broker::ConnectionObserver +{ +private: + typedef std::map<std::string, uint32_t> connectCountsMap_t; + enum CONNECTION_PROGRESS { C_CREATED=1, C_OPENED=2 }; + + Acl& acl; + uint32_t nameLimit; + uint32_t hostLimit; + qpid::sys::Mutex dataLock; + + connectCountsMap_t connectProgressMap; + connectCountsMap_t connectByNameMap; + connectCountsMap_t connectByHostMap; + + std::string getClientHost(const std::string mgmtId); + + bool limitCheckLH(connectCountsMap_t& theMap, + const std::string& theName, + uint32_t theLimit); + + void releaseLH(connectCountsMap_t& theMap, + const std::string& theName, + uint32_t theLimit); + +public: + ConnectionCounter(Acl& acl, uint32_t nl, uint32_t hl); + ~ConnectionCounter(); + + void connection(broker::Connection& connection); + void opened(broker::Connection& connection); + void closed(broker::Connection& connection); + +}; + +}} // namespace qpid::ha + +#endif /*!QPID_ACL_CONNECTIONCOUNTER_H*/ diff --git a/cpp/src/qpid/acl/AclData.cpp b/cpp/src/qpid/acl/AclData.cpp index 06fc223a73..da7f240a9b 100644 --- a/cpp/src/qpid/acl/AclData.cpp +++ b/cpp/src/qpid/acl/AclData.cpp @@ -24,152 +24,302 @@ namespace qpid { namespace acl { - AclData::AclData():decisionMode(qpid::acl::DENY),transferAcl(false),aclSource("UNKNOWN") + // + // constructor + // + AclData::AclData(): + decisionMode(qpid::acl::DENY), + transferAcl(false), + aclSource("UNKNOWN") { - for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){ + for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) + { actionList[cnt]=0; } - } + + // + // clear + // void AclData::clear () { - for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){ - if (actionList[cnt]){ + for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) + { + if (actionList[cnt]) + { for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++) - delete actionList[cnt][cnt1]; + delete actionList[cnt][cnt1]; } delete[] actionList[cnt]; } - } - bool AclData::matchProp(const std::string & src, const std::string& src1) + + // + // matchProp + // + // Compare a rule's property name with a lookup name, + // The rule's name may contain a trailing '*' to specify a wildcard match. + // + bool AclData::matchProp(const std::string& ruleStr, + const std::string& lookupStr) { - // allow wildcard on the end of strings... - if (src.data()[src.size()-1]=='*') { - return (src.compare(0, src.size()-1, src1, 0,src.size()-1 ) == 0); - } else { - return (src.compare(src1)==0) ; + // allow wildcard on the end of rule strings... + if (ruleStr.data()[ruleStr.size()-1]=='*') + { + return ruleStr.compare(0, + ruleStr.size()-1, + lookupStr, + 0, + ruleStr.size()-1 ) == 0; + } + else + { + return ruleStr.compare(lookupStr) == 0; } } - AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, - const std::string& name, std::map<Property, std::string>* params) { - - QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) - << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name - << " with params " << AclHelper::propertyMapToString(params)); - - AclResult aclresult = decisionMode; - if (actionList[action] && actionList[action][objType]) { - AclData::actObjItr itrRule = actionList[action][objType]->find(id); - if (itrRule == actionList[action][objType]->end()) - itrRule = actionList[action][objType]->find("*"); - - if (itrRule != actionList[action][objType]->end()) { - - QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); - - //loop the vector - for (ruleSetItr i = itrRule->second.begin(); i < itrRule->second.end(); i++) { - QPID_LOG(debug, "ACL: checking rule " << i->toString()); - // loop the names looking for match - bool match = true; - for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) { - //match name is exists first - if (pMItr->first == acl::PROP_NAME) { - if (matchProp(pMItr->second, name)){ - QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" - << pMItr->second << "' given in the rule"); - }else{ + + // + // lookup + // + // The ACL main business logic function of matching rules and declaring + // an allow or deny result. + // + AclResult AclData::lookup( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name, + std::map<Property, std::string>* params) + { + QPID_LOG(debug, "ACL: Lookup for id:" << id + << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) + << " name:" << name + << " with params " << AclHelper::propertyMapToString(params)); + + // A typical log looks like: + // ACL: Lookup for id:bob@QPID action:create objectType:queue name:q2 + // with params { durable=false passive=false autodelete=false + // exclusive=false alternate= policytype= maxqueuesize=0 + // maxqueuecount=0 } + + // Default result is blanket decision mode for the entire ACL list. + AclResult aclresult = decisionMode; + + // Test for lists of rules at the intersection of the Action & Object + if (actionList[action] && actionList[action][objType]) + { + // Find the list of rules for this actorId + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + + // If individual actorId not found then find a rule set for '*'. + if (itrRule == actionList[action][objType]->end()) + itrRule = actionList[action][objType]->find("*"); + + if (itrRule != actionList[action][objType]->end()) + { + // A list of rules exists for this actor/action/object tuple. + // Iterate the rule set to search for a matching rule. + ruleSetItr rsItr = itrRule->second.end(); + for (int cnt = itrRule->second.size(); cnt != 0; cnt--) + { + rsItr--; + + QPID_LOG(debug, "ACL: checking rule " << rsItr->toString()); + + bool match = true; + bool limitChecked = true; + + // Iterate this rule's properties. A 'match' is true when + // all of the rule's properties are found to be satisfied + // in the lookup param list. The lookup may specify things + // (they usually do) that are not in the rule properties but + // these things don't interfere with the rule match. + + for (specPropertyMapItr rulePropMapItr = rsItr->props.begin(); + (rulePropMapItr != rsItr->props.end()) && match; + rulePropMapItr++) + { + // The rule property map's NAME property is given in + // the calling args and not in the param map. + if (rulePropMapItr->first == acl::SPECPROP_NAME) + { + if (matchProp(rulePropMapItr->second, name)) + { + QPID_LOG(debug, "ACL: lookup name '" << name + << "' matched with rule name '" + << rulePropMapItr->second << "'"); + } + else + { + match = false; + QPID_LOG(debug, "ACL: lookup name '" << name + << "' didn't match with rule name '" + << rulePropMapItr->second << "'"); + } + } + else + { + if (params) + { + // The rule's property map non-NAME properties + // found in the lookup's params list. + // In some cases the param's index is not the same + // as rule's index. + propertyMapItr lookupParamItr; + switch (rulePropMapItr->first) + { + case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT: + case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT: + lookupParamItr = params->find(PROP_MAXQUEUECOUNT); + break; + + case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT: + case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT: + lookupParamItr = params->find(PROP_MAXQUEUESIZE); + break; + + default: + lookupParamItr = params->find((Property)rulePropMapItr->first); + break; + }; + + if (lookupParamItr == params->end()) + { + // Now the rule has a specified property + // that does not exist in the caller's + // lookup params list. + // This rule does not match. match = false; - QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" - << pMItr->second << "' given in the rule"); + QPID_LOG(debug, "ACL: lookup parameter map doesn't contain the rule property '" + << AclHelper::getPropertyStr(rulePropMapItr->first) << "'"); } - } else if (params) { //match pMItr against params - propertyMapItr paramItr = params->find(pMItr->first); - if (paramItr == params->end()) { - match = false; - QPID_LOG(debug, "ACL: the given parameter map in lookup doesn't contain the property '" - << AclHelper::getPropertyStr(pMItr->first) << "'"); - }else if ( pMItr->first == acl::PROP_MAXQUEUECOUNT || pMItr->first == acl::PROP_MAXQUEUESIZE ) { - if ( pMItr->first == paramItr->first ) { - - uint64_t aclMax = 0; - uint64_t paramMax = 0; - - try{ - aclMax = boost::lexical_cast<uint64_t>(pMItr->second); - }catch(const boost::bad_lexical_cast&){ - match = false; - QPID_LOG(error,"Error evaluating rule. " << - "Illegal value given in ACL source <" << aclSource << - "> for property '" << - AclHelper::getPropertyStr(pMItr->first) << "' : " << - boost::lexical_cast<std::string>(pMItr->second)); - break; - } - - try{ - paramMax = boost::lexical_cast<uint64_t>(paramItr->second); - }catch(const boost::bad_lexical_cast&){ + else + { + // Now account for the business of rules + // whose property indexes are mismatched. + switch (rulePropMapItr->first) + { + case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT: + case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT: + limitChecked &= + compareIntMax( + rulePropMapItr->first, + boost::lexical_cast<std::string>(rulePropMapItr->second), + boost::lexical_cast<std::string>(lookupParamItr->second)); + break; + + case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT: + case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT: + limitChecked &= + compareIntMin( + rulePropMapItr->first, + boost::lexical_cast<std::string>(rulePropMapItr->second), + boost::lexical_cast<std::string>(lookupParamItr->second)); + break; + + default: + if (matchProp(rulePropMapItr->second, lookupParamItr->second)) + { + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(lookupParamItr->first) + << "," << lookupParamItr->second + << ") given in lookup matched the pair(" + << AclHelper::getPropertyStr(rulePropMapItr->first) << "," + << rulePropMapItr->second + << ") given in the rule"); + } + else + { match = false; - QPID_LOG(error,"Error evaluating rule. " << - "Illegal value given in lookup for property '" << - AclHelper::getPropertyStr(pMItr->first) << "' : " << - boost::lexical_cast<std::string>(paramItr->second)); - break; - } - - QPID_LOG(debug, "ACL: Numeric comparison for property " << - AclHelper::getPropertyStr(paramItr->first) << - " (value given in lookup = " << - boost::lexical_cast<std::string>(paramItr->second) << - ", value give in rule = " << - boost::lexical_cast<std::string>(pMItr->second) << " )"); - - if (( aclMax ) && ( paramMax == 0 || paramMax > aclMax)){ - match = decisionMode == qpid::acl::ALLOW ; - QPID_LOG(debug, "ACL: Limit exceeded and match=" << - (match ? "true": "false") << - " as decision mode is " << AclHelper::getAclResultStr(decisionMode)); + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(lookupParamItr->first) + << "," << lookupParamItr->second + << ") given in lookup doesn't match the pair(" + << AclHelper::getPropertyStr(rulePropMapItr->first) + << "," << rulePropMapItr->second + << ") given in the rule"); } - } - }else if (matchProp(pMItr->second, paramItr->second)) { - QPID_LOG(debug, "ACL: the pair(" - << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second - << ") given in lookup matched the pair(" - << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); - } else { - QPID_LOG(debug, "ACL: the pair(" - << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second - << ") given in lookup doesn't match the pair(" - << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); - match = false; - } + break; + }; + } + } + else + { + // params don't exist. } } - if (match) + } + if (match) + { + aclresult = rsItr->ruleMode; + if (!limitChecked) { - aclresult = getACLResult(i->logOnly, i->log); - QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); - return aclresult; + // Now a lookup matched all rule properties but one + // of the numeric limit checks has failed. + // Demote allow rules to corresponding deny rules. + switch (aclresult) + { + case acl::ALLOW: + aclresult = acl::DENY; + break; + case acl::ALLOWLOG: + aclresult = acl::DENYLOG; + break; + default: + break; + }; } + QPID_LOG(debug,"ACL: Successful match, the decision is:" + << AclHelper::getAclResultStr(aclresult)); + return aclresult; + } + else + { + // This rule did not match the requested lookup and + // does not contribute to an ACL decision. } } } + else + { + // The Action-Object list has entries but not for this actorId + // nor for *. + } + } + else + { + // The Action-Object list has no entries. + } - QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); - return aclresult; + QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode " + << AclHelper::getAclResultStr(aclresult)); + return aclresult; } - AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey) + + // + // lookup + // + // The ACL main business logic function of matching rules and declaring + // an allow or deny result. + // + AclResult AclData::lookup( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& /*Exchange*/ name, + const std::string& routingKey) { - QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) - << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " exchange name:" << name - << " with routing key " << RoutingKey); + QPID_LOG(debug, "ACL: Lookup for id:" << id + << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) + << " exchange name:" << name + << " with routing key " << routingKey); AclResult aclresult = decisionMode; @@ -179,83 +329,183 @@ namespace acl { if (itrRule == actionList[action][objType]->end()) itrRule = actionList[action][objType]->find("*"); - if (itrRule != actionList[action][objType]->end() ) { - - QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); - + if (itrRule != actionList[action][objType]->end() ) + { //loop the vector - for (ruleSetItr i=itrRule->second.begin(); i<itrRule->second.end(); i++) { - QPID_LOG(debug, "ACL: checking rule " << i->toString()); + ruleSetItr rsItr = itrRule->second.end(); + for (int cnt = itrRule->second.size(); cnt != 0; cnt--) + { + rsItr--; + + QPID_LOG(debug, "ACL: checking rule " << rsItr->toString()); // loop the names looking for match bool match =true; - for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) + for (specPropertyMapItr pMItr = rsItr->props.begin(); + (pMItr != rsItr->props.end()) && match; + pMItr++) { //match name is exists first - if (pMItr->first == acl::PROP_NAME){ - if (matchProp(pMItr->second, name)){ - QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" - << pMItr->second << "' given in the rule"); + switch (pMItr->first) + { + case acl::SPECPROP_NAME: + if (matchProp(pMItr->second, name)) + { + QPID_LOG(debug, "ACL: lookup exchange name '" + << name << "' matched with rule name '" + << pMItr->second << "'"); - }else{ + } + else + { match= false; - QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" - << pMItr->second << "' given in the rule"); - } - }else if (pMItr->first == acl::PROP_ROUTINGKEY){ - if (matchProp(pMItr->second, RoutingKey)){ - QPID_LOG(debug, "ACL: name '" << name << "' matched with routing_key '" - << pMItr->second << "' given in the rule"); - }else{ + QPID_LOG(debug, "ACL: lookup exchange name '" + << name << "' did not match with rule name '" + << pMItr->second << "'"); + } + break; + + case acl::SPECPROP_ROUTINGKEY: + if (matchProp(pMItr->second, routingKey)) + { + QPID_LOG(debug, "ACL: lookup key name '" + << routingKey << "' matched with rule routing key '" + << pMItr->second << "'"); + } + else + { match= false; - QPID_LOG(debug, "ACL: name '" << name << "' didn't match with routing_key '" - << pMItr->second << "' given in the rule"); - } - } + QPID_LOG(debug, "ACL: lookup key name '" + << routingKey << "' did not match with rule routing key '" + << pMItr->second << "'"); + } + break; + + default: + // Don't care + break; + }; } if (match){ - aclresult = getACLResult(i->logOnly, i->log); - QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); + aclresult = rsItr->ruleMode; + QPID_LOG(debug,"ACL: Successful match, the decision is:" + << AclHelper::getAclResultStr(aclresult)); return aclresult; } } } } - QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); + QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode " + << AclHelper::getAclResultStr(aclresult)); return aclresult; } - AclResult AclData::getACLResult(bool logOnly, bool log) + // + // + // + AclData::~AclData() + { + clear(); + } + + + // + // Limit check a MAX int limit + // + bool AclData::compareIntMax(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue) { - switch (decisionMode) + uint64_t aclMax (0); + uint64_t paramMax (0); + + try { - case qpid::acl::ALLOWLOG: - case qpid::acl::ALLOW: - if (logOnly) return qpid::acl::ALLOWLOG; - if (log) - return qpid::acl::DENYLOG; - else - return qpid::acl::DENY; + aclMax = boost::lexical_cast<uint64_t>(theAclValue); + } + catch(const boost::bad_lexical_cast&) + { + assert (false); + return false; + } + + try + { + paramMax = boost::lexical_cast<uint64_t>(theLookupValue); + } + catch(const boost::bad_lexical_cast&) + { + QPID_LOG(error,"ACL: Error evaluating rule. " + << "Illegal value given in lookup for property '" + << AclHelper::getPropertyStr(theProperty) + << "' : " << theLookupValue); + return false; + } + QPID_LOG(debug, "ACL: Numeric greater-than comparison for property " + << AclHelper::getPropertyStr(theProperty) + << " (value given in lookup = " << theLookupValue + << ", value give in rule = " << theAclValue << " )"); - case qpid::acl::DENYLOG: - case qpid::acl::DENY: - if (logOnly) return qpid::acl::DENYLOG; - if (log) - return qpid::acl::ALLOWLOG; - else - return qpid::acl::ALLOW; + if (( aclMax ) && ( paramMax == 0 || paramMax > aclMax)) + { + QPID_LOG(debug, "ACL: Max limit exceeded for property '" + << AclHelper::getPropertyStr(theProperty) << "'"); + return false; } - QPID_LOG(error, "ACL Decision Failed, setting DENY"); - return qpid::acl::DENY; + return true; } - AclData::~AclData() + + // + // limit check a MIN int limit + // + bool AclData::compareIntMin(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue) { - clear(); + uint64_t aclMin (0); + uint64_t paramMin (0); + + try + { + aclMin = boost::lexical_cast<uint64_t>(theAclValue); + } + catch(const boost::bad_lexical_cast&) + { + assert (false); + return false; + } + + try + { + paramMin = boost::lexical_cast<uint64_t>(theLookupValue); + } + catch(const boost::bad_lexical_cast&) + { + QPID_LOG(error,"ACL: Error evaluating rule. " + << "Illegal value given in lookup for property '" + << AclHelper::getPropertyStr(theProperty) + << "' : " << theLookupValue); + return false; + } + + QPID_LOG(debug, "ACL: Numeric less-than comparison for property " + << AclHelper::getPropertyStr(theProperty) + << " (value given in lookup = " << theLookupValue + << ", value give in rule = " << theAclValue << " )"); + + if (( aclMin ) && ( paramMin == 0 || paramMin < aclMin)) + { + QPID_LOG(debug, "ACL: Min limit exceeded for property '" + << AclHelper::getPropertyStr(theProperty) << "'"); + return false; + } + + return true; } -}} +}} diff --git a/cpp/src/qpid/acl/AclData.h b/cpp/src/qpid/acl/AclData.h index 81125fdcbc..1c1cb3e9c6 100644 --- a/cpp/src/qpid/acl/AclData.h +++ b/cpp/src/qpid/acl/AclData.h @@ -33,50 +33,91 @@ class AclData { public: typedef std::map<qpid::acl::Property, std::string> propertyMap; - typedef propertyMap::const_iterator propertyMapItr; + typedef propertyMap::const_iterator propertyMapItr; + + typedef std::map<qpid::acl::SpecProperty, std::string> specPropertyMap; + typedef specPropertyMap::const_iterator specPropertyMapItr; + + // + // rule + // + // Created by AclReader and stored in a ruleSet vector for subsequent + // run-time lookup matching and allow/deny decisions. + // RuleSet vectors are indexed by Action-Object-actorId so these + // attributes are not part of a rule. + // A single ACL file entry may create many rule entries in + // many ruleset vectors. + // struct rule { - bool log; - bool logOnly; // this is a rule is to log only + int rawRuleNum; // rule number in ACL file + qpid::acl::AclResult ruleMode; // combined allow/deny log/nolog + specPropertyMap props; // - // key value map - //?? - propertyMap props; - - rule (propertyMap& p):log(false),logOnly(false),props(p) {}; + rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) : + rawRuleNum(ruleNum), + ruleMode(res), + props(p) + {}; std::string toString () const { std::ostringstream ruleStr; - ruleStr << "[log=" << log << ", logOnly=" << logOnly << " props{"; - for (propertyMapItr pMItr = props.begin(); pMItr != props.end(); pMItr++) { - ruleStr << " " << AclHelper::getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + ruleStr << "[rule " << rawRuleNum + << " ruleMode = " << AclHelper::getAclResultStr(ruleMode) + << " props{"; + for (specPropertyMapItr pMItr = props.begin(); + pMItr != props.end(); + pMItr++) { + ruleStr << " " + << AclHelper::getPropertyStr((SpecProperty) pMItr-> first) + << "=" << pMItr->second; } ruleStr << " }]"; return ruleStr.str(); } }; - typedef std::vector<rule> ruleSet; - typedef ruleSet::const_iterator ruleSetItr; - typedef std::map<std::string, ruleSet > actionObject; // user - typedef actionObject::iterator actObjItr; - typedef actionObject* aclAction; - // Action*[] -> Object*[] -> map<user -> set<Rule> > - aclAction* actionList[qpid::acl::ACTIONSIZE]; - qpid::acl::AclResult decisionMode; // determines if the rule set is a deny or allow mode. - bool transferAcl; - std::string aclSource; + typedef std::vector<rule> ruleSet; + typedef ruleSet::const_iterator ruleSetItr; + typedef std::map<std::string, ruleSet > actionObject; // user + typedef actionObject::iterator actObjItr; + typedef actionObject* aclAction; - AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0); - AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey); - AclResult getACLResult(bool logOnly, bool log); + // Action*[] -> Object*[] -> map<user -> set<Rule> > + aclAction* actionList[qpid::acl::ACTIONSIZE]; + qpid::acl::AclResult decisionMode; // allow/deny[-log] if no matching rule found + bool transferAcl; + std::string aclSource; + + AclResult lookup( + const std::string& id, // actor id + const Action& action, + const ObjectType& objType, + const std::string& name, // object name + std::map<Property, std::string>* params=0); + + AclResult lookup( + const std::string& id, // actor id + const Action& action, + const ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey); bool matchProp(const std::string & src, const std::string& src1); void clear (); AclData(); virtual ~AclData(); + +private: + bool compareIntMax(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue); + + bool compareIntMin(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue); }; }} // namespace qpid::acl diff --git a/cpp/src/qpid/acl/AclPlugin.cpp b/cpp/src/qpid/acl/AclPlugin.cpp index d611797c49..6c18cd2749 100644 --- a/cpp/src/qpid/acl/AclPlugin.cpp +++ b/cpp/src/qpid/acl/AclPlugin.cpp @@ -40,7 +40,9 @@ struct AclOptions : public Options { AclOptions(AclValues& v) : Options("ACL Options"), values(v) { addOptions() - ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir"); + ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir") + ("acl-max-connect-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user") + ("acl-max-connect-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address"); } }; diff --git a/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp index 74358a20c1..80debf1bd1 100644 --- a/cpp/src/qpid/acl/AclReader.cpp +++ b/cpp/src/qpid/acl/AclReader.cpp @@ -49,7 +49,7 @@ namespace acl { objStatus = ALL; } - bool AclReader::aclRule::addProperty(const Property p, const std::string v) { + bool AclReader::aclRule::addProperty(const SpecProperty p, const std::string v) { return props.insert(propNvPair(p, v)).second; } @@ -85,146 +85,108 @@ namespace acl { void AclReader::loadDecisionData(boost::shared_ptr<AclData> d) { d->clear(); - QPID_LOG(debug, "ACL Load Rules"); - int cnt = rules.size(); + QPID_LOG(debug, "ACL: Load Rules"); bool foundmode = false; - for (rlCitr i = rules.end(); cnt; cnt--) { + rlCitr i = rules.end(); + for (int cnt = rules.size(); cnt; cnt--) { i--; - QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) + QPID_LOG(debug, "ACL: Processing " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1 && (*((*i)->names.begin())).compare("*") == 0) { d->decisionMode = (*i)->res; - QPID_LOG(debug, "ACL FoundMode " + QPID_LOG(debug, "ACL: FoundMode " << AclHelper::getAclResultStr(d->decisionMode)); foundmode = true; } else { - AclData::rule rule((*i)->props); - bool addrule = true; - - switch ((*i)->res) { - case qpid::acl::ALLOWLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::ALLOW || - d->decisionMode == qpid::acl::ALLOWLOG) - rule.logOnly = true; - break; - case qpid::acl::ALLOW: - if (d->decisionMode == qpid::acl::ALLOW || - d->decisionMode == qpid::acl::ALLOWLOG) - addrule = false; - break; - case qpid::acl::DENYLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::DENY || - d->decisionMode == qpid::acl::DENYLOG) - rule.logOnly = true; - break; - case qpid::acl::DENY: - if (d->decisionMode == qpid::acl::DENY || - d->decisionMode == qpid::acl::DENYLOG) - addrule = false; - break; - default: - throw Exception("Invalid ACL Result loading rules."); - } + AclData::rule rule(cnt, (*i)->res, (*i)->props); // Action -> Object -> map<user -> set<Rule> > - if (addrule) { - std::ostringstream actionstr; - for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); - acnt < acl::ACTIONSIZE; - (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) { - - if (acnt == acl::ACT_PUBLISH) - d->transferAcl = true; // we have transfer ACL - - actionstr << AclHelper::getActionStr((Action) acnt) << ","; - - //find the Action, create if not exist - if (d->actionList[acnt] == NULL) { - d->actionList[acnt] = - new AclData::aclAction[qpid::acl::OBJECTSIZE]; - for (int j = 0; j < qpid::acl::OBJECTSIZE; j++) - d->actionList[acnt][j] = NULL; - } - - // optimize this loop to limit to valid options only!! - for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 - : (*i)->object); - ocnt < acl::OBJECTSIZE; - (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { - - //find the Object, create if not exist - if (d->actionList[acnt][ocnt] == NULL) - d->actionList[acnt][ocnt] = - new AclData::actionObject; - - // add users and Rule to object set - bool allNames = false; - // check to see if names.begin is '*' - if ((*(*i)->names.begin()).compare("*") == 0) - allNames = true; - - for (nsCitr itr = (allNames ? names.begin() - : (*i)->names.begin()); - itr != (allNames ? names.end() : (*i)->names.end()); - itr++) { - - AclData::actObjItr itrRule = - d->actionList[acnt][ocnt]->find(*itr); - - if (itrRule == d->actionList[acnt][ocnt]->end()) { - AclData::ruleSet rSet; - rSet.push_back(rule); - d->actionList[acnt][ocnt]->insert - (make_pair(std::string(*itr), rSet)); - } else { - // TODO add code to check for dead rules - // allow peter create queue name=tmp <-- dead rule!! - // allow peter create queue - - itrRule->second.push_back(rule); - } - } - - } + std::ostringstream actionstr; + for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); + acnt < acl::ACTIONSIZE; + (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) { + + if (acnt == acl::ACT_PUBLISH) + d->transferAcl = true; // we have transfer ACL + + actionstr << AclHelper::getActionStr((Action) acnt) << ","; + + //find the Action, create if not exist + if (d->actionList[acnt] == NULL) { + d->actionList[acnt] = + new AclData::aclAction[qpid::acl::OBJECTSIZE]; + for (int j = 0; j < qpid::acl::OBJECTSIZE; j++) + d->actionList[acnt][j] = NULL; } - std::ostringstream objstr; - for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object); + // TODO: optimize this loop to limit to valid options only!! + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 + : (*i)->object); ocnt < acl::OBJECTSIZE; - (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { - objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ","; + (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + + //find the Object, create if not exist + if (d->actionList[acnt][ocnt] == NULL) + d->actionList[acnt][ocnt] = + new AclData::actionObject; + + // add users and Rule to object set + bool allNames = false; + // check to see if names.begin is '*' + if ((*(*i)->names.begin()).compare("*") == 0) + allNames = true; + + for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); + itr != (allNames ? names.end() : (*i)->names.end()); + itr++) { + AclData::actObjItr itrRule = + d->actionList[acnt][ocnt]->find(*itr); + + if (itrRule == d->actionList[acnt][ocnt]->end()) { + AclData::ruleSet rSet; + rSet.push_back(rule); + d->actionList[acnt][ocnt]->insert + (make_pair(std::string(*itr), rSet)); + } else { + // TODO add code to check for dead rules + // allow peter create queue name=tmp <-- dead rule!! + // allow peter create queue + + itrRule->second.push_back(rule); + } + } } + } - bool allNames = ((*(*i)->names.begin()).compare("*") == 0); - std::ostringstream userstr; - for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); - itr != (allNames ? names.end() : (*i)->names.end()); - itr++) { - userstr << *itr << ","; - } + std::ostringstream objstr; + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object); + ocnt < acl::OBJECTSIZE; + (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ","; + } - QPID_LOG(debug, "ACL: Adding actions {" << - actionstr.str().substr(0,actionstr.str().length()-1) - << "} to objects {" << - objstr.str().substr(0,objstr.str().length()-1) - << "} with props " << - AclHelper::propertyMapToString(&rule.props) - << " for users {" << - userstr.str().substr(0,userstr.str().length()-1) - << "}" ); - } else { - QPID_LOG(debug, "ACL Skipping based on Mode:" - << AclHelper::getAclResultStr(d->decisionMode)); + bool allNames = ((*(*i)->names.begin()).compare("*") == 0); + std::ostringstream userstr; + for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); + itr != (allNames ? names.end() : (*i)->names.end()); + itr++) { + userstr << *itr << ","; } - } + QPID_LOG(debug, "ACL: Adding actions {" << + actionstr.str().substr(0,actionstr.str().length()-1) + << "} to objects {" << + objstr.str().substr(0,objstr.str().length()-1) + << "} with props " << + AclHelper::propertyMapToString(&rule.props) + << " for users {" << + userstr.str().substr(0,userstr.str().length()-1) + << "}" ); + } } - } @@ -277,7 +239,7 @@ namespace acl { } ifs.close(); if (err) return -3; - QPID_LOG(notice, "Read ACL file \"" << fn << "\""); + QPID_LOG(notice, "ACL: Read file \"" << fn << "\""); } catch (const std::exception& e) { errorStream << "Unable to read ACL file \"" << fn << "\": " << e.what(); ifs.close(); @@ -410,8 +372,8 @@ namespace acl { // Debug aid void AclReader::printNames() const { - QPID_LOG(debug, "Group list: " << groups.size() << " groups found:" ); - std::string tmp; + QPID_LOG(debug, "ACL: Group list: " << groups.size() << " groups found:" ); + std::string tmp("ACL: "); for (gmCitr i=groups.begin(); i!= groups.end(); i++) { tmp += " \""; tmp += i->first; @@ -421,10 +383,10 @@ namespace acl { tmp += *j; } QPID_LOG(debug, tmp); - tmp.clear(); + tmp = "ACL: "; } - QPID_LOG(debug, "Name list: " << names.size() << " names found:" ); - tmp.clear(); + QPID_LOG(debug, "ACL: name list: " << names.size() << " names found:" ); + tmp = "ACL: "; for (nsCitr k=names.begin(); k!=names.end(); k++) { tmp += " "; tmp += *k; @@ -501,9 +463,9 @@ namespace acl { << propNvp.first << "\". (Must be name=value)"; return false; } - Property prop; + SpecProperty prop; try { - prop = AclHelper::getProperty(propNvp.first); + prop = AclHelper::getSpecProperty(propNvp.first); } catch (...) { errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber << ", Unknown property \"" << propNvp.first << "\"."; @@ -532,10 +494,10 @@ namespace acl { // Debug aid void AclReader::printRules() const { - QPID_LOG(debug, "Rule list: " << rules.size() << " ACL rules found:"); - int cnt = 0; + QPID_LOG(debug, "ACL: Rule list: " << rules.size() << " ACL rules found:"); + int cnt = 1; for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) { - QPID_LOG(debug, " " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); + QPID_LOG(debug, "ACL: " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); } } diff --git a/cpp/src/qpid/acl/AclReader.h b/cpp/src/qpid/acl/AclReader.h index 62c6f38f37..730013f4ed 100644 --- a/cpp/src/qpid/acl/AclReader.h +++ b/cpp/src/qpid/acl/AclReader.h @@ -33,65 +33,71 @@ namespace qpid { namespace acl { class AclReader { - typedef std::set<std::string> nameSet; - typedef nameSet::const_iterator nsCitr; - typedef boost::shared_ptr<nameSet> nameSetPtr; - - typedef std::pair<std::string, nameSetPtr> groupPair; - typedef std::map<std::string, nameSetPtr> groupMap; - typedef groupMap::const_iterator gmCitr; - typedef std::pair<gmCitr, bool> gmRes; - - typedef std::pair<Property, std::string> propNvPair; - typedef std::map<Property, std::string> propMap; - typedef propMap::const_iterator pmCitr; - + typedef std::set<std::string> nameSet; + typedef nameSet::const_iterator nsCitr; + typedef boost::shared_ptr<nameSet> nameSetPtr; + + typedef std::pair<std::string, nameSetPtr> groupPair; + typedef std::map<std::string, nameSetPtr> groupMap; + typedef groupMap::const_iterator gmCitr; + typedef std::pair<gmCitr, bool> gmRes; + + typedef std::pair<SpecProperty, std::string> propNvPair; + typedef std::map<SpecProperty, std::string> propMap; + typedef propMap::const_iterator pmCitr; + + // + // aclRule + // + // A temporary rule created during ACL file processing. + // class aclRule { public: enum objectStatus {NONE, VALUE, ALL}; - AclResult res; - nameSet names; - bool actionAll; // True if action is set to keyword "all" - Action action; // Ignored if action is set to keyword "all" - objectStatus objStatus; - ObjectType object; // Ignored for all status values except VALUE - propMap props; + + AclResult res; + nameSet names; + bool actionAll; // True if action is set to keyword "all" + Action action; // Ignored if action is set to keyword "all" + objectStatus objStatus; + ObjectType object; // Ignored for all status values except VALUE + propMap props; public: aclRule(const AclResult r, const std::string n, const groupMap& groups); // action = "all" aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a); void setObjectType(const ObjectType o); void setObjectTypeAll(); - bool addProperty(const Property p, const std::string v); + bool addProperty(const SpecProperty p, const std::string v); bool validate(const AclHelper::objectMapPtr& validationMap); std::string toString(); // debug aid private: void processName(const std::string& name, const groupMap& groups); }; - typedef boost::shared_ptr<aclRule> aclRulePtr; - typedef std::vector<aclRulePtr> ruleList; - typedef ruleList::const_iterator rlCitr; + typedef boost::shared_ptr<aclRule> aclRulePtr; + typedef std::vector<aclRulePtr> ruleList; + typedef ruleList::const_iterator rlCitr; - typedef std::vector<std::string> tokList; - typedef tokList::const_iterator tlCitr; + typedef std::vector<std::string> tokList; + typedef tokList::const_iterator tlCitr; - typedef std::set<std::string> keywordSet; - typedef keywordSet::const_iterator ksCitr; + typedef std::set<std::string> keywordSet; + typedef keywordSet::const_iterator ksCitr; typedef std::pair<std::string, std::string> nvPair; // Name-Value pair - std::string fileName; - int lineNumber; - bool contFlag; - std::string groupName; - nameSet names; - groupMap groups; - ruleList rules; + std::string fileName; + int lineNumber; + bool contFlag; + std::string groupName; + nameSet names; + groupMap groups; + ruleList rules; AclHelper::objectMapPtr validationMap; - std::ostringstream errorStream; + std::ostringstream errorStream; public: AclReader(); virtual ~AclReader(); - int read(const std::string& fn, boost::shared_ptr<AclData> d); + int read(const std::string& fn, boost::shared_ptr<AclData> d); // return=0 for success std::string getError(); private: diff --git a/cpp/src/qpid/acl/AclValidator.cpp b/cpp/src/qpid/acl/AclValidator.cpp index d5a00b005b..49bb65db4b 100644 --- a/cpp/src/qpid/acl/AclValidator.cpp +++ b/cpp/src/qpid/acl/AclValidator.cpp @@ -29,7 +29,7 @@ namespace qpid { namespace acl { - AclValidator::IntPropertyType::IntPropertyType(int64_t i,int64_t j) : min(i), max(j){ + AclValidator::IntPropertyType::IntPropertyType(int64_t i,int64_t j) : min(i), max(j){ } bool AclValidator::IntPropertyType::validate(const std::string& val) { @@ -49,12 +49,12 @@ namespace acl { } std::string AclValidator::IntPropertyType::allowedValues() { - return "values should be between " + + return "values should be between " + boost::lexical_cast<std::string>(min) + " and " + boost::lexical_cast<std::string>(max); } - AclValidator::EnumPropertyType::EnumPropertyType(std::vector<std::string>& allowed): values(allowed){ + AclValidator::EnumPropertyType::EnumPropertyType(std::vector<std::string>& allowed): values(allowed){ } bool AclValidator::EnumPropertyType::validate(const std::string& val) { @@ -78,24 +78,27 @@ namespace acl { } AclValidator::AclValidator(){ - validators.insert(Validator(acl::PROP_MAXQUEUESIZE, - boost::shared_ptr<PropertyType>( - new IntPropertyType(0,std::numeric_limits<int64_t>::max())) - ) - ); - - validators.insert(Validator(acl::PROP_MAXQUEUECOUNT, - boost::shared_ptr<PropertyType>( - new IntPropertyType(0,std::numeric_limits<int64_t>::max())) - ) - ); + validators.insert(Validator(acl::SPECPROP_MAXQUEUESIZELOWERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); + + validators.insert(Validator(acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); + + validators.insert(Validator(acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); + + validators.insert(Validator(acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); std::string policyTypes[] = {"ring", "ring_strict", "flow_to_disk", "reject"}; std::vector<std::string> v(policyTypes, policyTypes + sizeof(policyTypes) / sizeof(std::string)); - validators.insert(Validator(acl::PROP_POLICYTYPE, - boost::shared_ptr<PropertyType>(new EnumPropertyType(v)) - ) - ); + validators.insert(Validator(acl::SPECPROP_POLICYTYPE, + boost::shared_ptr<PropertyType>( + new EnumPropertyType(v)))); } @@ -114,9 +117,9 @@ namespace acl { if (d->actionList[cnt][cnt1]){ std::for_each(d->actionList[cnt][cnt1]->begin(), - d->actionList[cnt][cnt1]->end(), - boost::bind(&AclValidator::validateRuleSet, this, _1)); - }//if + d->actionList[cnt][cnt1]->end(), + boost::bind(&AclValidator::validateRuleSet, this, _1)); + }//if }//for }//if }//for @@ -125,25 +128,29 @@ namespace acl { void AclValidator::validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules){ std::for_each(rules.second.begin(), rules.second.end(), - boost::bind(&AclValidator::validateRule, this, _1)); + boost::bind(&AclValidator::validateRule, this, _1)); } void AclValidator::validateRule(qpid::acl::AclData::rule& rule){ std::for_each(rule.props.begin(), rule.props.end(), - boost::bind(&AclValidator::validateProperty, this, _1)); + boost::bind(&AclValidator::validateProperty, this, _1)); } - void AclValidator::validateProperty(std::pair<const qpid::acl::Property, std::string>& prop){ + void AclValidator::validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop){ ValidatorItr itr = validators.find(prop.first); if (itr != validators.end()){ - QPID_LOG(debug,"Found validator for property " << itr->second->allowedValues()); + QPID_LOG(debug,"ACL: Found validator for property '" << acl::AclHelper::getPropertyStr(itr->first) + << "'. " << itr->second->allowedValues()); if (!itr->second->validate(prop.second)){ - throw Exception( prop.second + " is not a valid value for '" + + QPID_LOG(debug, "ACL: Property failed validation. '" << prop.second << "' is not a valid value for '" + << AclHelper::getPropertyStr(prop.first) << "'"); + + throw Exception( prop.second + " is not a valid value for '" + AclHelper::getPropertyStr(prop.first) + "', " + itr->second->allowedValues()); - } + } } } diff --git a/cpp/src/qpid/acl/AclValidator.h b/cpp/src/qpid/acl/AclValidator.h index 966e5d326b..f85c241b06 100644 --- a/cpp/src/qpid/acl/AclValidator.h +++ b/cpp/src/qpid/acl/AclValidator.h @@ -33,18 +33,18 @@ namespace acl { class AclValidator { /* Base Property */ - class PropertyType{ - + class PropertyType{ + public: virtual ~PropertyType(){}; virtual bool validate(const std::string& val)=0; virtual std::string allowedValues()=0; }; - class IntPropertyType : public PropertyType{ + class IntPropertyType : public PropertyType{ int64_t min; int64_t max; - + public: IntPropertyType(int64_t min,int64_t max); virtual ~IntPropertyType (){}; @@ -53,7 +53,7 @@ class AclValidator { }; class EnumPropertyType : public PropertyType{ - std::vector<std::string> values; + std::vector<std::string> values; public: EnumPropertyType(std::vector<std::string>& allowed); @@ -61,23 +61,23 @@ class AclValidator { virtual bool validate(const std::string& val); virtual std::string allowedValues(); }; - - typedef std::pair<acl::Property,boost::shared_ptr<PropertyType> > Validator; - typedef std::map<acl::Property,boost::shared_ptr<PropertyType> > ValidatorMap; + + typedef std::pair<acl::SpecProperty,boost::shared_ptr<PropertyType> > Validator; + typedef std::map<acl::SpecProperty,boost::shared_ptr<PropertyType> > ValidatorMap; typedef ValidatorMap::iterator ValidatorItr; - + ValidatorMap validators; public: void validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules); void validateRule(qpid::acl::AclData::rule& rule); - void validateProperty(std::pair<const qpid::acl::Property, std::string>& prop); - void validate(boost::shared_ptr<AclData> d); + void validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop); + void validate(boost::shared_ptr<AclData> d); AclValidator(); ~AclValidator(); }; - + }} // namespace qpid::acl #endif // QPID_ACL_ACLVALIDATOR_H diff --git a/cpp/src/qpid/acl/management-schema.xml b/cpp/src/qpid/acl/management-schema.xml index 7f48a9be34..19fe37333c 100644 --- a/cpp/src/qpid/acl/management-schema.xml +++ b/cpp/src/qpid/acl/management-schema.xml @@ -23,8 +23,40 @@ <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/> <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/> <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/> + <statistic name="connectionDenyCount" type="count64" unit="connection" desc="Number of connections denied"/> <method name="reloadACLFile" desc="Reload the ACL file"/> + + <!-- + Lookup is a general object lookup + User Name + Action + Object + Object Name + Property Map consisting of <"name" "value"> string pairs. + --> + <method name="Lookup" desc="Lookup: user action object [objectName [propertyMap]]"> + <arg name="userId" dir="I" type="lstr"/> + <arg name="action" dir="I" type="lstr"/> + <arg name="object" dir="I" type="lstr"/> + <arg name="objectName" dir="I" type="lstr"/> + <arg name="propertyMap" dir="I" type="map"/> + <arg name="result" dir="O" type="lstr"/> + </method> + + <!-- + LookupPublish is a specific lookup for a PUBLISH EXCHANGE fastpath + User Name + Exchange Name + Routing Key + --> + <method name="LookupPublish" desc="Lookup PUBLISH EXCHANGE: user exchangeName routingKey"> + <arg name="userId" dir="I" type="lstr"/> + <arg name="exchangeName" dir="I" type="lstr"/> + <arg name="routingKey" dir="I" type="lstr"/> + <arg name="result" dir="O" type="lstr"/> + </method> + </class> <eventArguments> @@ -34,10 +66,12 @@ <arg name="objectType" type="sstr"/> <arg name="reason" type="lstr"/> <arg name="userId" type="sstr"/> + <arg name="clientAddr" type="sstr"/> </eventArguments> <event name="allow" sev="inform" args="userId, action, objectType, objectName, arguments"/> <event name="deny" sev="notice" args="userId, action, objectType, objectName, arguments"/> + <event name="connectionDeny" sev="notice" args="userId, clientAddr"/> <event name="fileLoaded" sev="inform" args="userId"/> <event name="fileLoadFailed" sev="error" args="userId, reason"/> diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/cpp/src/qpid/agent/ManagementAgentImpl.cpp index f183ff8e0c..09b7fa58e9 100644 --- a/cpp/src/qpid/agent/ManagementAgentImpl.cpp +++ b/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -31,9 +31,11 @@ #include <fstream> #include <boost/lexical_cast.hpp> +namespace qpid { +namespace management { + using namespace qpid::client; using namespace qpid::framing; -using namespace qpid::management; using namespace qpid::sys; using namespace std; using std::stringstream; @@ -1260,7 +1262,7 @@ void ManagementAgentImpl::ConnectionThread::run() int totalSleep = 0; do { sys::Mutex::ScopedUnlock _unlock(connLock); - ::sleep(delayMin); + qpid::sys::sleep(delayMin); totalSleep += delayMin; } while (totalSleep < delay && !shutdown); sleeping = false; @@ -1396,8 +1398,11 @@ void ManagementAgentImpl::PublishThread::run() sleepTime = 1; while (totalSleep < agent.getInterval() && !shutdown) { - ::sleep(sleepTime); + qpid::sys::sleep(sleepTime); totalSleep += sleepTime; } } } + +}} + diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h index e32ff266b9..ff9281b6fc 100644 --- a/cpp/src/qpid/broker/AclModule.h +++ b/cpp/src/qpid/broker/AclModule.h @@ -22,6 +22,7 @@ #include "qpid/RefCounted.h" +#include "qpid/Exception.h" #include <boost/shared_ptr.hpp> #include <map> #include <set> @@ -32,17 +33,81 @@ namespace qpid { namespace acl { - enum ObjectType {OBJ_QUEUE, OBJ_EXCHANGE, OBJ_BROKER, OBJ_LINK, - OBJ_METHOD, OBJECTSIZE}; // OBJECTSIZE must be last in list - enum Action {ACT_CONSUME, ACT_PUBLISH, ACT_CREATE, ACT_ACCESS, ACT_BIND, - ACT_UNBIND, ACT_DELETE, ACT_PURGE, ACT_UPDATE, - ACTIONSIZE}; // ACTIONSIZE must be last in list - enum Property {PROP_NAME, PROP_DURABLE, PROP_OWNER, PROP_ROUTINGKEY, - PROP_PASSIVE, PROP_AUTODELETE, PROP_EXCLUSIVE, PROP_TYPE, - PROP_ALTERNATE, PROP_QUEUENAME, PROP_SCHEMAPACKAGE, - PROP_SCHEMACLASS, PROP_POLICYTYPE, PROP_MAXQUEUESIZE, - PROP_MAXQUEUECOUNT}; - enum AclResult {ALLOW, ALLOWLOG, DENY, DENYLOG}; + // Interface enumerations. + // These enumerations define enum lists and implied text strings + // to match. They are used in two areas: + // 1. In the ACL specifications in the ACL file, file parsing, and + // internal rule storage. + // 2. In the authorize interface in the rest of the broker where + // code requests the ACL module to authorize an action. + + // ObjectType shared between ACL spec and ACL authorise interface + enum ObjectType { + OBJ_QUEUE, + OBJ_EXCHANGE, + OBJ_BROKER, + OBJ_LINK, + OBJ_METHOD, + OBJECTSIZE }; // OBJECTSIZE must be last in list + + // Action shared between ACL spec and ACL authorise interface + enum Action { + ACT_CONSUME, + ACT_PUBLISH, + ACT_CREATE, + ACT_ACCESS, + ACT_BIND, + ACT_UNBIND, + ACT_DELETE, + ACT_PURGE, + ACT_UPDATE, + ACTIONSIZE }; // ACTIONSIZE must be last in list + + // Property used in ACL authorize interface + enum Property { + PROP_NAME, + PROP_DURABLE, + PROP_OWNER, + PROP_ROUTINGKEY, + PROP_AUTODELETE, + PROP_EXCLUSIVE, + PROP_TYPE, + PROP_ALTERNATE, + PROP_QUEUENAME, + PROP_SCHEMAPACKAGE, + PROP_SCHEMACLASS, + PROP_POLICYTYPE, + PROP_MAXQUEUESIZE, + PROP_MAXQUEUECOUNT }; + + // Property used in ACL spec file + // Note for properties common to file processing/rule storage and to + // broker rule lookups the identical enum values are used. + enum SpecProperty { + SPECPROP_NAME = PROP_NAME, + SPECPROP_DURABLE = PROP_DURABLE, + SPECPROP_OWNER = PROP_OWNER, + SPECPROP_ROUTINGKEY = PROP_ROUTINGKEY, + SPECPROP_AUTODELETE = PROP_AUTODELETE, + SPECPROP_EXCLUSIVE = PROP_EXCLUSIVE, + SPECPROP_TYPE = PROP_TYPE, + SPECPROP_ALTERNATE = PROP_ALTERNATE, + SPECPROP_QUEUENAME = PROP_QUEUENAME, + SPECPROP_SCHEMAPACKAGE = PROP_SCHEMAPACKAGE, + SPECPROP_SCHEMACLASS = PROP_SCHEMACLASS, + SPECPROP_POLICYTYPE = PROP_POLICYTYPE, + + SPECPROP_MAXQUEUESIZELOWERLIMIT, + SPECPROP_MAXQUEUESIZEUPPERLIMIT, + SPECPROP_MAXQUEUECOUNTLOWERLIMIT, + SPECPROP_MAXQUEUECOUNTUPPERLIMIT }; + +// AclResult shared between ACL spec and ACL authorise interface + enum AclResult { + ALLOW, + ALLOWLOG, + DENY, + DENYLOG }; } // namespace acl @@ -54,14 +119,25 @@ namespace broker { public: - // effienty turn off ACL on message transfer. + // Some ACLs are invoked on every message transfer. + // doTransferAcl pervents time consuming ACL calls on a per-message basis. virtual bool doTransferAcl()=0; - virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& name, + virtual bool authorise( + const std::string& id, + const acl::Action& action, + const acl::ObjectType& objType, + const std::string& name, std::map<acl::Property, std::string>* params=0)=0; - virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& ExchangeName, - const std::string& RoutingKey)=0; - // create specilied authorise methods for cases that need faster matching as needed. + + virtual bool authorise( + const std::string& id, + const acl::Action& action, + const acl::ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey)=0; + + // Add specialized authorise() methods as required. virtual ~AclModule() {}; }; @@ -79,7 +155,7 @@ namespace acl { if (str.compare("broker") == 0) return OBJ_BROKER; if (str.compare("link") == 0) return OBJ_LINK; if (str.compare("method") == 0) return OBJ_METHOD; - throw str; + throw qpid::Exception(str); } static inline std::string getObjectTypeStr(const ObjectType o) { switch (o) { @@ -102,7 +178,7 @@ namespace acl { if (str.compare("delete") == 0) return ACT_DELETE; if (str.compare("purge") == 0) return ACT_PURGE; if (str.compare("update") == 0) return ACT_UPDATE; - throw str; + throw qpid::Exception(str); } static inline std::string getActionStr(const Action a) { switch (a) { @@ -124,7 +200,6 @@ namespace acl { if (str.compare("durable") == 0) return PROP_DURABLE; if (str.compare("owner") == 0) return PROP_OWNER; if (str.compare("routingkey") == 0) return PROP_ROUTINGKEY; - if (str.compare("passive") == 0) return PROP_PASSIVE; if (str.compare("autodelete") == 0) return PROP_AUTODELETE; if (str.compare("exclusive") == 0) return PROP_EXCLUSIVE; if (str.compare("type") == 0) return PROP_TYPE; @@ -134,8 +209,8 @@ namespace acl { if (str.compare("schemaclass") == 0) return PROP_SCHEMACLASS; if (str.compare("policytype") == 0) return PROP_POLICYTYPE; if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE; - if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT; - throw str; + if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT; + throw qpid::Exception(str); } static inline std::string getPropertyStr(const Property p) { switch (p) { @@ -143,7 +218,6 @@ namespace acl { case PROP_DURABLE: return "durable"; case PROP_OWNER: return "owner"; case PROP_ROUTINGKEY: return "routingkey"; - case PROP_PASSIVE: return "passive"; case PROP_AUTODELETE: return "autodelete"; case PROP_EXCLUSIVE: return "exclusive"; case PROP_TYPE: return "type"; @@ -153,17 +227,61 @@ namespace acl { case PROP_SCHEMACLASS: return "schemaclass"; case PROP_POLICYTYPE: return "policytype"; case PROP_MAXQUEUESIZE: return "maxqueuesize"; - case PROP_MAXQUEUECOUNT: return "maxqueuecount"; + case PROP_MAXQUEUECOUNT: return "maxqueuecount"; default: assert(false); // should never get here } return ""; } + static inline SpecProperty getSpecProperty(const std::string& str) { + if (str.compare("name") == 0) return SPECPROP_NAME; + if (str.compare("durable") == 0) return SPECPROP_DURABLE; + if (str.compare("owner") == 0) return SPECPROP_OWNER; + if (str.compare("routingkey") == 0) return SPECPROP_ROUTINGKEY; + if (str.compare("autodelete") == 0) return SPECPROP_AUTODELETE; + if (str.compare("exclusive") == 0) return SPECPROP_EXCLUSIVE; + if (str.compare("type") == 0) return SPECPROP_TYPE; + if (str.compare("alternate") == 0) return SPECPROP_ALTERNATE; + if (str.compare("queuename") == 0) return SPECPROP_QUEUENAME; + if (str.compare("schemapackage") == 0) return SPECPROP_SCHEMAPACKAGE; + if (str.compare("schemaclass") == 0) return SPECPROP_SCHEMACLASS; + if (str.compare("policytype") == 0) return SPECPROP_POLICYTYPE; + if (str.compare("queuemaxsizelowerlimit") == 0) return SPECPROP_MAXQUEUESIZELOWERLIMIT; + if (str.compare("queuemaxsizeupperlimit") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT; + if (str.compare("queuemaxcountlowerlimit") == 0) return SPECPROP_MAXQUEUECOUNTLOWERLIMIT; + if (str.compare("queuemaxcountupperlimit") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT; + // Allow old names in ACL file as aliases for newly-named properties + if (str.compare("maxqueuesize") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT; + if (str.compare("maxqueuecount") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT; + throw qpid::Exception(str); + } + static inline std::string getPropertyStr(const SpecProperty p) { + switch (p) { + case SPECPROP_NAME: return "name"; + case SPECPROP_DURABLE: return "durable"; + case SPECPROP_OWNER: return "owner"; + case SPECPROP_ROUTINGKEY: return "routingkey"; + case SPECPROP_AUTODELETE: return "autodelete"; + case SPECPROP_EXCLUSIVE: return "exclusive"; + case SPECPROP_TYPE: return "type"; + case SPECPROP_ALTERNATE: return "alternate"; + case SPECPROP_QUEUENAME: return "queuename"; + case SPECPROP_SCHEMAPACKAGE: return "schemapackage"; + case SPECPROP_SCHEMACLASS: return "schemaclass"; + case SPECPROP_POLICYTYPE: return "policytype"; + case SPECPROP_MAXQUEUESIZELOWERLIMIT: return "queuemaxsizelowerlimit"; + case SPECPROP_MAXQUEUESIZEUPPERLIMIT: return "queuemaxsizeupperlimit"; + case SPECPROP_MAXQUEUECOUNTLOWERLIMIT: return "queuemaxcountlowerlimit"; + case SPECPROP_MAXQUEUECOUNTUPPERLIMIT: return "queuemaxcountupperlimit"; + default: assert(false); // should never get here + } + return ""; + } static inline AclResult getAclResult(const std::string& str) { if (str.compare("allow") == 0) return ALLOW; if (str.compare("allow-log") == 0) return ALLOWLOG; if (str.compare("deny") == 0) return DENY; if (str.compare("deny-log") == 0) return DENYLOG; - throw str; + throw qpid::Exception(str); } static inline std::string getAclResultStr(const AclResult r) { switch (r) { @@ -187,8 +305,11 @@ namespace acl { typedef boost::shared_ptr<objectMap> objectMapPtr; typedef std::map<Property, std::string> propMap; typedef propMap::const_iterator propMapItr; + typedef std::map<SpecProperty, std::string> specPropMap; + typedef specPropMap::const_iterator specPropMapItr; - // This map contains the legal combinations of object/action/properties found in an ACL file + // This map contains the legal combinations of object/action/properties + // found in an ACL file static void loadValidationMap(objectMapPtr& map) { if (!map.get()) return; map->clear(); @@ -199,7 +320,6 @@ namespace acl { propSetPtr p1(new propSet); p1->insert(PROP_TYPE); p1->insert(PROP_ALTERNATE); - p1->insert(PROP_PASSIVE); p1->insert(PROP_DURABLE); propSetPtr p2(new propSet); @@ -224,7 +344,6 @@ namespace acl { propSetPtr p4(new propSet); p4->insert(PROP_ALTERNATE); - p4->insert(PROP_PASSIVE); p4->insert(PROP_DURABLE); p4->insert(PROP_EXCLUSIVE); p4->insert(PROP_AUTODELETE); @@ -260,21 +379,31 @@ namespace acl { map->insert(objectPair(OBJ_METHOD, a4)); } - static std::string propertyMapToString(const std::map<Property, std::string>* params) { + // + // properyMapToString + // + template <typename T> + static std::string propertyMapToString( + const std::map<T, std::string>* params) + { std::ostringstream ss; ss << "{"; if (params) { - for (propMapItr pMItr = params->begin(); pMItr != params->end(); pMItr++) { - ss << " " << getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + for (typename std::map<T, std::string>::const_iterator + pMItr = params->begin(); pMItr != params->end(); pMItr++) + { + ss << " " << getPropertyStr((T) pMItr-> first) + << "=" << pMItr->second; } } ss << " }"; return ss.str(); } + }; - + }} // namespace qpid::acl #endif // QPID_ACLMODULE_ACL_H diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp index 9a1f4be468..5b531e4636 100644 --- a/cpp/src/qpid/broker/Bridge.cpp +++ b/cpp/src/qpid/broker/Bridge.cpp @@ -62,7 +62,7 @@ Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l, InitializeCallback init) : link(_link), id(_id), args(_args), mgmtObject(0), listener(l), name(Uuid(true).str()), queueName("qpid.bridge_queue_"), persistenceId(0), - initialize(init) + initialize(init), detached(false) { std::stringstream title; title << id << "_" << name; @@ -85,11 +85,14 @@ Bridge::~Bridge() void Bridge::create(Connection& c) { + detached = false; // Reset detached in case we are recovering. connState = &c; conn = &c; FieldTable options; if (args.i_sync) options.setInt("qpid.sync_frequency", args.i_sync); SessionHandler& sessionHandler = c.getChannel(id); + sessionHandler.setDetachedCallback( + boost::bind(&Bridge::sessionDetached, shared_from_this())); if (args.i_srcIsLocal) { if (args.i_dynamic) throw Exception("Dynamic routing not supported for push routes"); @@ -179,12 +182,6 @@ void Bridge::destroy() listener(this); } -bool Bridge::isSessionReady() const -{ - SessionHandler& sessionHandler = conn->getChannel(id); - return sessionHandler.ready(); -} - void Bridge::setPersistenceId(uint64_t pId) const { persistenceId = pId; @@ -336,4 +333,8 @@ const string& Bridge::getLocalTag() const return link->getBroker()->getFederationTag(); } +void Bridge::sessionDetached() { + detached = true; +} + }} diff --git a/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h index b849b11ba8..32b9fd1781 100644 --- a/cpp/src/qpid/broker/Bridge.h +++ b/cpp/src/qpid/broker/Bridge.h @@ -33,6 +33,7 @@ #include "qmf/org/apache/qpid/broker/Bridge.h" #include <boost/function.hpp> +#include <boost/enable_shared_from_this.hpp> #include <memory> namespace qpid { @@ -44,7 +45,10 @@ class Link; class LinkRegistry; class SessionHandler; -class Bridge : public PersistableConfig, public management::Manageable, public Exchange::DynamicBridge +class Bridge : public PersistableConfig, + public management::Manageable, + public Exchange::DynamicBridge, + public boost::enable_shared_from_this<Bridge> { public: typedef boost::shared_ptr<Bridge> shared_ptr; @@ -63,7 +67,7 @@ public: void destroy(); bool isDurable() { return args.i_durable; } - bool isSessionReady() const; + bool isDetached() const { return detached; } management::ManagementObject* GetManagementObject() const; management::Manageable::status_t ManagementMethod(uint32_t methodId, @@ -90,6 +94,9 @@ public: const qmf::org::apache::qpid::broker::ArgsLinkBridge& getArgs() { return args; } private: + // Callback when the bridge's session is detached. + void sessionDetached(); + struct PushHandler : framing::FrameHandler { PushHandler(Connection* c) { conn = c; } void handle(framing::AMQFrame& frame); @@ -112,7 +119,7 @@ private: ConnectionState* connState; Connection* conn; InitializeCallback initialize; - + bool detached; // Set when session is detached. bool resetProxy(); }; diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp index 0fd31580f6..f20cce18a2 100644 --- a/cpp/src/qpid/broker/Broker.cpp +++ b/cpp/src/qpid/broker/Broker.cpp @@ -111,6 +111,7 @@ Broker::Options::Options(const std::string& name) : maxConnections(500), connectionBacklog(10), enableMgmt(1), + mgmtPublish(1), mgmtPubInterval(10), queueCleanInterval(60*10),//10 minutes auth(SaslAuthenticator::available()), @@ -148,6 +149,7 @@ Broker::Options::Options(const std::string& name) : ("max-connections", optValue(maxConnections, "N"), "Sets the maximum allowed connections") ("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket") ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management") + ("mgmt-publish", optValue(mgmtPublish,"yes|no"), "Enable Publish of Management Data ('no' implies query-only)") ("mgmt-qmf2", optValue(qmf2Support,"yes|no"), "Enable broadcast of management information over QMF v2") ("mgmt-qmf1", optValue(qmf1Support,"yes|no"), "Enable broadcast of management information over QMF v1") // FIXME aconway 2012-02-13: consistent treatment of values in SECONDS @@ -213,7 +215,7 @@ Broker::Broker(const Broker::Options& conf) : try { if (conf.enableMgmt) { QPID_LOG(info, "Management enabled"); - managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(), + managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(), conf.mgmtPublish, conf.mgmtPubInterval, this, conf.workerThreads + 3); managementAgent->setName("apache.org", "qpidd"); _qmf::Package packageInitializer(managementAgent.get()); @@ -228,6 +230,7 @@ Broker::Broker(const Broker::Options& conf) : mgmtObject->set_maxConns(conf.maxConnections); mgmtObject->set_connBacklog(conf.connectionBacklog); mgmtObject->set_mgmtPubInterval(conf.mgmtPubInterval); + mgmtObject->set_mgmtPublish(conf.mgmtPublish); mgmtObject->set_version(qpid::version); if (dataDir.isEnabled()) mgmtObject->set_dataDir(dataDir.getPath()); @@ -885,7 +888,6 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( if (acl) { std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _FALSE)); params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); params.insert(make_pair(acl::PROP_EXCLUSIVE, owner ? _TRUE : _FALSE)); params.insert(make_pair(acl::PROP_AUTODELETE, autodelete ? _TRUE : _FALSE)); @@ -956,7 +958,6 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange( std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_TYPE, type)); params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _FALSE)); params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_EXCHANGE,name,¶ms) ) throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << userId)); diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h index cff38eecdd..135b9340f9 100644 --- a/cpp/src/qpid/broker/Broker.h +++ b/cpp/src/qpid/broker/Broker.h @@ -106,6 +106,7 @@ public: int maxConnections; int connectionBacklog; bool enableMgmt; + bool mgmtPublish; uint16_t mgmtPubInterval; uint16_t queueCleanInterval; bool auth; @@ -206,7 +207,7 @@ public: ConsumerFactories consumerFactories; public: - virtual ~Broker(); + QPID_BROKER_EXTERN virtual ~Broker(); QPID_BROKER_EXTERN Broker(const Options& configuration); static QPID_BROKER_EXTERN boost::intrusive_ptr<Broker> create(const Options& configuration); @@ -218,16 +219,16 @@ public: * port, which will be different if the configured port is * 0. */ - virtual uint16_t getPort(const std::string& name) const; + QPID_BROKER_EXTERN virtual uint16_t getPort(const std::string& name) const; /** * Run the broker. Implements Runnable::run() so the broker * can be run in a separate thread. */ - virtual void run(); + QPID_BROKER_EXTERN virtual void run(); /** Shut down the broker */ - virtual void shutdown(); + QPID_BROKER_EXTERN virtual void shutdown(); QPID_BROKER_EXTERN void setStore (boost::shared_ptr<MessageStore>& store); void setAsyncStore(boost::shared_ptr<AsyncStore>& asyncStore); @@ -248,14 +249,14 @@ public: SessionManager& getSessionManager() { return sessionManager; } const std::string& getFederationTag() const { return federationTag; } - management::ManagementObject* GetManagementObject (void) const; - management::Manageable* GetVhostObject (void) const; - management::Manageable::status_t ManagementMethod (uint32_t methodId, - management::Args& args, - std::string& text); + QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject() const; + QPID_BROKER_EXTERN management::Manageable* GetVhostObject() const; + QPID_BROKER_EXTERN management::Manageable::status_t ManagementMethod( + uint32_t methodId, management::Args& args, std::string& text); /** Add to the broker's protocolFactorys */ - void registerProtocolFactory(const std::string& name, boost::shared_ptr<sys::ProtocolFactory>); + QPID_BROKER_EXTERN void registerProtocolFactory( + const std::string& name, boost::shared_ptr<sys::ProtocolFactory>); /** Accept connections */ QPID_BROKER_EXTERN void accept(); @@ -273,15 +274,17 @@ public: /** Move messages from one queue to another. A zero quantity means to move all messages */ - uint32_t queueMoveMessages( const std::string& srcQueue, - const std::string& destQueue, - uint32_t qty, - const qpid::types::Variant::Map& filter); + QPID_BROKER_EXTERN uint32_t queueMoveMessages( + const std::string& srcQueue, + const std::string& destQueue, + uint32_t qty, + const qpid::types::Variant::Map& filter); - boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory(const std::string& name = TCP_TRANSPORT) const; + QPID_BROKER_EXTERN boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory( + const std::string& name = TCP_TRANSPORT) const; /** Expose poller so plugins can register their descriptors. */ - boost::shared_ptr<sys::Poller> getPoller(); + QPID_BROKER_EXTERN boost::shared_ptr<sys::Poller> getPoller(); boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; } void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; } @@ -291,7 +294,7 @@ public: /** Timer for tasks that must be synchronized if we are in a cluster */ sys::Timer& getClusterTimer() { return clusterTimer.get() ? *clusterTimer : timer; } - void setClusterTimer(std::auto_ptr<sys::Timer>); + QPID_BROKER_EXTERN void setClusterTimer(std::auto_ptr<sys::Timer>); boost::function<std::vector<Url> ()> getKnownBrokers; @@ -322,15 +325,14 @@ public: * context. *@return true if delivery of a message should be deferred. */ - boost::function<bool (const std::string& queue, - const boost::intrusive_ptr<Message>& msg)> deferDelivery; + boost::function<bool (const std::string& queue, const boost::intrusive_ptr<Message>& msg)> deferDelivery; bool isAuthenticating ( ) { return config.auth; } bool isTimestamping() { return config.timestampRcvMsgs; } typedef boost::function1<void, boost::shared_ptr<Queue> > QueueFunctor; - std::pair<boost::shared_ptr<Queue>, bool> createQueue( + QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Queue>, bool> createQueue( const std::string& name, bool durable, bool autodelete, @@ -339,30 +341,39 @@ public: const qpid::framing::FieldTable& arguments, const std::string& userId, const std::string& connectionId); - void deleteQueue(const std::string& name, - const std::string& userId, - const std::string& connectionId, - QueueFunctor check = QueueFunctor()); - std::pair<Exchange::shared_ptr, bool> createExchange( + + QPID_BROKER_EXTERN void deleteQueue( + const std::string& name, + const std::string& userId, + const std::string& connectionId, + QueueFunctor check = QueueFunctor()); + + QPID_BROKER_EXTERN std::pair<Exchange::shared_ptr, bool> createExchange( const std::string& name, const std::string& type, bool durable, const std::string& alternateExchange, const qpid::framing::FieldTable& args, const std::string& userId, const std::string& connectionId); - void deleteExchange(const std::string& name, const std::string& userId, - const std::string& connectionId); - void bind(const std::string& queue, - const std::string& exchange, - const std::string& key, - const qpid::framing::FieldTable& arguments, - const std::string& userId, - const std::string& connectionId); - void unbind(const std::string& queue, - const std::string& exchange, - const std::string& key, - const std::string& userId, - const std::string& connectionId); + + QPID_BROKER_EXTERN void deleteExchange( + const std::string& name, const std::string& userId, + const std::string& connectionId); + + QPID_BROKER_EXTERN void bind( + const std::string& queue, + const std::string& exchange, + const std::string& key, + const qpid::framing::FieldTable& arguments, + const std::string& userId, + const std::string& connectionId); + + QPID_BROKER_EXTERN void unbind( + const std::string& queue, + const std::string& exchange, + const std::string& key, + const std::string& userId, + const std::string& connectionId); ConsumerFactories& getConsumerFactories() { return consumerFactories; } ConnectionObservers& getConnectionObservers() { return connectionObservers; } diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp index 1e6aab217c..5e339cec03 100644 --- a/cpp/src/qpid/broker/Connection.cpp +++ b/cpp/src/qpid/broker/Connection.cpp @@ -185,11 +185,13 @@ void Connection::recordFromServer(const framing::AMQFrame& frame) // Don't record management stats in cluster-unsafe contexts if (mgmtObject != 0 && isClusterSafe()) { - mgmtObject->inc_framesToClient(); - mgmtObject->inc_bytesToClient(frame.encodedSize()); + qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics(); + cStats->framesToClient += 1; + cStats->bytesToClient += frame.encodedSize(); if (isMessage(frame.getMethod())) { - mgmtObject->inc_msgsToClient(); + cStats->msgsToClient += 1; } + mgmtObject->statisticsUpdated(); } } @@ -198,11 +200,13 @@ void Connection::recordFromClient(const framing::AMQFrame& frame) // Don't record management stats in cluster-unsafe contexts if (mgmtObject != 0 && isClusterSafe()) { - mgmtObject->inc_framesFromClient(); - mgmtObject->inc_bytesFromClient(frame.encodedSize()); + qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics(); + cStats->framesFromClient += 1; + cStats->bytesFromClient += frame.encodedSize(); if (isMessage(frame.getMethod())) { - mgmtObject->inc_msgsFromClient(); + cStats->msgsFromClient += 1; } + mgmtObject->statisticsUpdated(); } } diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h index 858ab6f7f4..1b8bd83139 100644 --- a/cpp/src/qpid/broker/Connection.h +++ b/cpp/src/qpid/broker/Connection.h @@ -113,15 +113,20 @@ class Connection : public sys::ConnectionInputHandler, void requestIOProcessing (boost::function0<void>); void recordFromServer (const framing::AMQFrame& frame); void recordFromClient (const framing::AMQFrame& frame); + + // gets for configured federation links std::string getAuthMechanism(); std::string getAuthCredentials(); std::string getUsername(); std::string getPassword(); std::string getHost(); uint16_t getPort(); + void notifyConnectionForced(const std::string& text); void setUserId(const std::string& uid); void raiseConnectEvent(); + + // credentials for connected client const std::string& getUserId() const { return ConnectionState::getUserId(); } const std::string& getMgmtId() const { return mgmtId; } management::ManagementAgent* getAgent() const { return agent; } diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp index f1d43c5cdb..6894324117 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.cpp +++ b/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -28,6 +28,7 @@ #include "qpid/framing/AllInvoker.h" #include "qpid/framing/ConnectionStartOkBody.h" #include "qpid/framing/enum.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include "qpid/sys/SecurityLayer.h" #include "qpid/broker/AclModule.h" diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h index 05c5f00c57..2e25543308 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.h +++ b/cpp/src/qpid/broker/ConnectionHandler.h @@ -35,7 +35,6 @@ #include "qpid/framing/ProtocolInitiation.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/Exception.h" -#include "qpid/broker/AclModule.h" #include "qpid/sys/SecurityLayer.h" diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp index 5591539853..5d9aea7509 100644 --- a/cpp/src/qpid/broker/DirectExchange.cpp +++ b/cpp/src/qpid/broker/DirectExchange.cpp @@ -153,8 +153,9 @@ bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, c return true; } -void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +void DirectExchange::route(Deliverable& msg) { + const string& routingKey = msg.getMessage().getRoutingKey(); PreRoute pr(msg, this); ConstBindingList b; { diff --git a/cpp/src/qpid/broker/DirectExchange.h b/cpp/src/qpid/broker/DirectExchange.h index a6f9cf91af..833be52c1c 100644 --- a/cpp/src/qpid/broker/DirectExchange.h +++ b/cpp/src/qpid/broker/DirectExchange.h @@ -57,9 +57,7 @@ public: const std::string& routingKey, const qpid::framing::FieldTable* args); virtual bool unbind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(boost::shared_ptr<Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args); diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp index febd547478..d482c2c327 100644 --- a/cpp/src/qpid/broker/DtxManager.cpp +++ b/cpp/src/qpid/broker/DtxManager.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/DtxManager.h" #include "qpid/broker/DtxTimeout.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/StructHelper.h" #include "qpid/log/Statement.h" #include "qpid/sys/Timer.h" #include "qpid/ptr_map.h" @@ -55,7 +56,7 @@ void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionCon bool DtxManager::prepare(const std::string& xid) { - QPID_LOG(debug, "preparing: " << xid); + QPID_LOG(debug, "preparing: " << convert(xid)); try { return getWork(xid)->prepare(); } catch (DtxTimeoutException& e) { @@ -66,7 +67,7 @@ bool DtxManager::prepare(const std::string& xid) bool DtxManager::commit(const std::string& xid, bool onePhase) { - QPID_LOG(debug, "committing: " << xid); + QPID_LOG(debug, "committing: " << convert(xid)); try { bool result = getWork(xid)->commit(onePhase); remove(xid); @@ -79,7 +80,7 @@ bool DtxManager::commit(const std::string& xid, bool onePhase) void DtxManager::rollback(const std::string& xid) { - QPID_LOG(debug, "rolling back: " << xid); + QPID_LOG(debug, "rolling back: " << convert(xid)); try { getWork(xid)->rollback(); remove(xid); @@ -94,7 +95,7 @@ DtxWorkRecord* DtxManager::getWork(const std::string& xid) Mutex::ScopedLock locker(lock); WorkMap::iterator i = work.find(xid); if (i == work.end()) { - throw NotFoundException(QPID_MSG("Unrecognised xid " << xid)); + throw NotFoundException(QPID_MSG("Unrecognised xid " << convert(xid))); } return ptr_map_ptr(i); } @@ -109,7 +110,7 @@ void DtxManager::remove(const std::string& xid) Mutex::ScopedLock locker(lock); WorkMap::iterator i = work.find(xid); if (i == work.end()) { - throw NotFoundException(QPID_MSG("Unrecognised xid " << xid)); + throw NotFoundException(QPID_MSG("Unrecognised xid " << convert(xid))); } else { work.erase(i); } @@ -120,7 +121,7 @@ DtxWorkRecord* DtxManager::createWork(const std::string& xid) Mutex::ScopedLock locker(lock); WorkMap::iterator i = work.find(xid); if (i != work.end()) { - throw NotAllowedException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)")); + throw NotAllowedException(QPID_MSG("Xid " << convert(xid) << " is already known (use 'join' to add work to an existing xid)")); } else { std::string ncxid = xid; // Work around const correctness problems in ptr_map. return ptr_map_ptr(work.insert(ncxid, new DtxWorkRecord(ncxid, store)).first); @@ -175,3 +176,19 @@ void DtxManager::setStore (TransactionalStore* _store) { store = _store; } + +std::string DtxManager::convert(const qpid::framing::Xid& xid) +{ + qpid::framing::StructHelper helper; + std::string encoded; + helper.encode(xid, encoded); + return encoded; +} + +qpid::framing::Xid DtxManager::convert(const std::string& xid) +{ + qpid::framing::StructHelper helper; + qpid::framing::Xid decoded; + helper.decode(decoded, xid); + return decoded; +} diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h index 11895695a3..6f03189f66 100644 --- a/cpp/src/qpid/broker/DtxManager.h +++ b/cpp/src/qpid/broker/DtxManager.h @@ -26,6 +26,7 @@ #include "qpid/broker/DtxWorkRecord.h" #include "qpid/broker/TransactionalStore.h" #include "qpid/framing/amqp_types.h" +#include "qpid/framing/Xid.h" #include "qpid/sys/Mutex.h" #include "qpid/ptr_map.h" @@ -74,6 +75,8 @@ public: } DtxWorkRecord* getWork(const std::string& xid); bool exists(const std::string& xid); + static std::string convert(const framing::Xid& xid); + static framing::Xid convert(const std::string& xid); }; } diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp index a413fe418d..2c26fec49f 100644 --- a/cpp/src/qpid/broker/DtxWorkRecord.cpp +++ b/cpp/src/qpid/broker/DtxWorkRecord.cpp @@ -19,6 +19,7 @@ * */ #include "qpid/broker/DtxWorkRecord.h" +#include "qpid/broker/DtxManager.h" #include "qpid/framing/reply_exceptions.h" #include <boost/format.hpp> #include <boost/mem_fn.hpp> @@ -73,7 +74,7 @@ bool DtxWorkRecord::commit(bool onePhase) if (prepared) { //already prepared i.e. 2pc if (onePhase) { - throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has been prepared, one-phase option not valid!")); + throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has been prepared, one-phase option not valid!")); } store->commit(*txn); @@ -84,7 +85,7 @@ bool DtxWorkRecord::commit(bool onePhase) } else { //1pc commit optimisation, don't need a 2pc transaction context: if (!onePhase) { - throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!")); + throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has not been prepared, one-phase option required!")); } std::auto_ptr<TransactionContext> localtxn = store->begin(); if (prepare(localtxn.get())) { @@ -116,10 +117,10 @@ void DtxWorkRecord::add(DtxBuffer::shared_ptr ops) { Mutex::ScopedLock locker(lock); if (expired) { - throw DtxTimeoutException(QPID_MSG("Branch with xid " << xid << " has timed out.")); + throw DtxTimeoutException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has timed out.")); } if (completed) { - throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!")); + throw CommandInvalidException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has been completed!")); } work.push_back(ops); } @@ -133,7 +134,7 @@ bool DtxWorkRecord::check() //iterate through all DtxBuffers and ensure they are all ended for (Work::iterator i = work.begin(); i != work.end(); i++) { if (!(*i)->isEnded()) { - throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " not completed!")); + throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " not completed!")); } else if ((*i)->isRollbackOnly()) { rolledback = true; } diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp index ecaa492903..8d20b0df81 100644 --- a/cpp/src/qpid/broker/Exchange.cpp +++ b/cpp/src/qpid/broker/Exchange.cpp @@ -32,7 +32,9 @@ #include "qpid/sys/ExceptionHolder.h" #include <stdexcept> -using namespace qpid::broker; +namespace qpid { +namespace broker { + using namespace qpid::framing; using qpid::framing::Buffer; using qpid::framing::FieldTable; @@ -135,20 +137,23 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); + qmf::org::apache::qpid::broker::Exchange::PerThreadStats *eStats = mgmtExchange->getStatistics(); + uint64_t contentSize = msg.contentSize(); + + eStats->msgReceives += 1; + eStats->byteReceives += contentSize; if (count == 0) { //QPID_LOG(warning, "Exchange " << getName() << " could not route message; no matching binding found"); - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); + eStats->msgDrops += 1; + eStats->byteDrops += contentSize; if (brokerMgmtObject) brokerMgmtObject->inc_discardsNoRoute(); } else { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + eStats->msgRoutes += count; + eStats->byteRoutes += count * contentSize; } } } @@ -156,7 +161,7 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) void Exchange::routeIVE(){ if (ive && lastMsg.get()){ DeliverableMessage dmsg(lastMsg); - route(dmsg, lastMsg->getRoutingKey(), lastMsg->getApplicationHeaders()); + route(dmsg); } } @@ -399,9 +404,12 @@ void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) { bool Exchange::routeWithAlternate(Deliverable& msg) { - route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); + route(msg); if (!msg.delivered && alternate) { - alternate->route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); + alternate->route(msg); } return msg.delivered; } + +}} + diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h index 9179dd5c7c..7376f814ed 100644 --- a/cpp/src/qpid/broker/Exchange.h +++ b/cpp/src/qpid/broker/Exchange.h @@ -196,7 +196,7 @@ public: virtual bool unbind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; virtual bool isBound(boost::shared_ptr<Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args) = 0; QPID_BROKER_EXTERN virtual void setProperties(const boost::intrusive_ptr<Message>&); - virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual void route(Deliverable& msg) = 0; //PersistableExchange: QPID_BROKER_EXTERN void setPersistenceId(uint64_t id) const; diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp index fca77f7ddd..43d7268dfb 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.cpp +++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -24,6 +24,7 @@ #include "qpid/broker/FanOutExchange.h" #include "qpid/broker/HeadersExchange.h" #include "qpid/broker/TopicExchange.h" +#include "qpid/broker/Link.h" #include "qpid/management/ManagementDirectExchange.h" #include "qpid/management/ManagementTopicExchange.h" #include "qpid/framing/reply_exceptions.h" @@ -58,6 +59,8 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c exchange = Exchange::shared_ptr(new ManagementDirectExchange(name, durable, args, parent, broker)); }else if (type == ManagementTopicExchange::typeName) { exchange = Exchange::shared_ptr(new ManagementTopicExchange(name, durable, args, parent, broker)); + }else if (type == Link::exchangeTypeName) { + exchange = Link::linkExchangeFactory(name); }else{ FunctionMap::iterator i = factory.find(type); if (i == factory.end()) { diff --git a/cpp/src/qpid/broker/ExchangeRegistry.h b/cpp/src/qpid/broker/ExchangeRegistry.h index 90ef81b49e..27b705fbe5 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.h +++ b/cpp/src/qpid/broker/ExchangeRegistry.h @@ -54,7 +54,7 @@ class ExchangeRegistry{ bool durable, const qpid::framing::FieldTable& args = framing::FieldTable()); QPID_BROKER_EXTERN void destroy(const std::string& name); - Exchange::shared_ptr getDefault(); + QPID_BROKER_EXTERN Exchange::shared_ptr getDefault(); /** * Find the named exchange. Return 0 if not found. @@ -75,7 +75,7 @@ class ExchangeRegistry{ /** Register an exchange instance. *@return true if registered, false if exchange with same name is already registered. */ - bool registerExchange(const Exchange::shared_ptr&); + QPID_BROKER_EXTERN bool registerExchange(const Exchange::shared_ptr&); QPID_BROKER_EXTERN void registerType(const std::string& type, FactoryFunction); @@ -85,7 +85,7 @@ class ExchangeRegistry{ for (ExchangeMap::const_iterator i = exchanges.begin(); i != exchanges.end(); ++i) f(i->second); } - + private: typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap; typedef std::map<std::string, FactoryFunction > FunctionMap; diff --git a/cpp/src/qpid/broker/Fairshare.cpp b/cpp/src/qpid/broker/Fairshare.cpp index 313aa746f1..7cdad1a44f 100644 --- a/cpp/src/qpid/broker/Fairshare.cpp +++ b/cpp/src/qpid/broker/Fairshare.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/Fairshare.h" #include "qpid/broker/QueuedMessage.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include <boost/format.hpp> #include <boost/lexical_cast.hpp> diff --git a/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp index 5879fa0892..2bce99b6fe 100644 --- a/cpp/src/qpid/broker/FanOutExchange.cpp +++ b/cpp/src/qpid/broker/FanOutExchange.cpp @@ -101,7 +101,7 @@ bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, cons return true; } -void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/) +void FanOutExchange::route(Deliverable& msg) { PreRoute pr(msg, this); doRoute(msg, bindings.snapshot()); diff --git a/cpp/src/qpid/broker/FanOutExchange.h b/cpp/src/qpid/broker/FanOutExchange.h index 1a7d486796..c979fdca25 100644 --- a/cpp/src/qpid/broker/FanOutExchange.h +++ b/cpp/src/qpid/broker/FanOutExchange.h @@ -54,9 +54,7 @@ class FanOutExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp index 142c23f276..6648ae0422 100644 --- a/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/cpp/src/qpid/broker/HeadersExchange.cpp @@ -191,8 +191,9 @@ bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, } -void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args) +void HeadersExchange::route(Deliverable& msg) { + const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (!args) { //can't match if there were no headers passed in if (mgmtExchange != 0) { diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h index 3b939d6851..d10892b9cc 100644 --- a/cpp/src/qpid/broker/HeadersExchange.h +++ b/cpp/src/qpid/broker/HeadersExchange.h @@ -98,9 +98,7 @@ class HeadersExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, diff --git a/cpp/src/qpid/broker/LegacyLVQ.cpp b/cpp/src/qpid/broker/LegacyLVQ.cpp index 49c0a32c19..f1deddf4c8 100644 --- a/cpp/src/qpid/broker/LegacyLVQ.cpp +++ b/cpp/src/qpid/broker/LegacyLVQ.cpp @@ -28,16 +28,26 @@ namespace broker { LegacyLVQ::LegacyLVQ(const std::string& k, bool b, Broker* br) : MessageMap(k), noBrowse(b), broker(br) {} void LegacyLVQ::setNoBrowse(bool b) -{ +{ noBrowse = b; } +bool LegacyLVQ::deleted(const QueuedMessage& message) +{ + Ordering::iterator i = messages.find(message.position); + if (i != messages.end() && i->second.payload == message.payload) { + erase(i); + return true; + } else { + return false; + } +} bool LegacyLVQ::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { Ordering::iterator i = messages.find(position); - if (i != messages.end() && i->second.payload == message.payload) { + if (i != messages.end() && i->second.payload == message.payload && i->second.status == QueuedMessage::AVAILABLE) { + i->second.status = QueuedMessage::ACQUIRED; message = i->second; - erase(i); return true; } else { return false; @@ -66,12 +76,17 @@ bool LegacyLVQ::push(const QueuedMessage& added, QueuedMessage& removed) } const QueuedMessage& LegacyLVQ::replace(const QueuedMessage& original, const QueuedMessage& update) -{ +{ //add the new message into the original position of the replaced message Ordering::iterator i = messages.find(original.position); - i->second = update; - i->second.position = original.position; - return i->second; + if (i != messages.end()) { + i->second = update; + i->second.position = original.position; + return i->second; + } else { + QPID_LOG(error, "Failed to replace message at " << original.position); + return update; + } } void LegacyLVQ::removeIf(Predicate p) diff --git a/cpp/src/qpid/broker/LegacyLVQ.h b/cpp/src/qpid/broker/LegacyLVQ.h index 695e51131d..9355069f37 100644 --- a/cpp/src/qpid/broker/LegacyLVQ.h +++ b/cpp/src/qpid/broker/LegacyLVQ.h @@ -40,6 +40,7 @@ class LegacyLVQ : public MessageMap { public: LegacyLVQ(const std::string& key, bool noBrowse = false, Broker* broker = 0); + bool deleted(const QueuedMessage&); bool acquire(const framing::SequenceNumber&, QueuedMessage&); bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); bool push(const QueuedMessage& added, QueuedMessage& removed); diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp index 4af1e6d6bd..f21c861149 100644 --- a/cpp/src/qpid/broker/Link.cpp +++ b/cpp/src/qpid/broker/Link.cpp @@ -31,6 +31,8 @@ #include "qpid/framing/enum.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/broker/AclModule.h" +#include "qpid/broker/Exchange.h" +#include "qpid/UrlArray.h" namespace qpid { namespace broker { @@ -48,6 +50,13 @@ using std::stringstream; using std::string; namespace _qmf = ::qmf::org::apache::qpid::broker; + +namespace { + const std::string FAILOVER_EXCHANGE("amq.failover"); + const std::string FAILOVER_HEADER_KEY("amq.failover"); +} + + struct LinkTimerTask : public sys::TimerTask { LinkTimerTask(Link& l, sys::Timer& t) : TimerTask(int64_t(l.getBroker()->getOptions().linkMaintenanceInterval* @@ -65,6 +74,57 @@ struct LinkTimerTask : public sys::TimerTask { sys::Timer& timer; }; + + +/** LinkExchange is used by the link to subscribe to the remote broker's amq.failover exchange. + */ +class LinkExchange : public broker::Exchange +{ +public: + LinkExchange(const std::string& name) : Exchange(name), link(0) {} + ~LinkExchange() {}; + std::string getType() const { return Link::exchangeTypeName; } + + // Exchange methods - set up to prevent binding/unbinding etc from clients! + bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*) { return false; } + bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*) { return false; } + bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const) {return false;} + + // Process messages sent from the remote's amq.failover exchange by extracting the failover URLs + // and saving them should the Link need to reconnect. + void route(broker::Deliverable& msg) + { + if (!link) return; + const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders(); + framing::Array addresses; + if (headers && headers->getArray(FAILOVER_HEADER_KEY, addresses)) { + // convert the Array of addresses to a single Url container for used with setUrl(): + std::vector<Url> urlVec; + Url urls; + urlVec = urlArrayToVector(addresses); + for(size_t i = 0; i < urlVec.size(); ++i) + urls.insert(urls.end(), urlVec[i].begin(), urlVec[i].end()); + QPID_LOG(debug, "Remote broker has provided these failover addresses= " << urls); + link->setUrl(urls); + } + } + + void setLink(Link *_link) + { + assert(!link); + link = _link; + } + +private: + Link *link; +}; + + +boost::shared_ptr<Exchange> Link::linkExchangeFactory( const std::string& _name ) +{ + return Exchange::shared_ptr(new LinkExchange(_name)); +} + Link::Link(LinkRegistry* _links, MessageStore* _store, const string& _host, @@ -76,8 +136,9 @@ Link::Link(LinkRegistry* _links, const string& _password, Broker* _broker, Manageable* parent) - : links(_links), store(_store), host(_host), port(_port), - transport(_transport), + : links(_links), store(_store), + configuredTransport(_transport), configuredHost(_host), configuredPort(_port), + host(_host), port(_port), transport(_transport), durable(_durable), authMechanism(_authMechanism), username(_username), password(_password), persistenceId(0), mgmtObject(0), broker(_broker), state(0), @@ -88,7 +149,8 @@ Link::Link(LinkRegistry* _links, channelCounter(1), connection(0), agent(0), - timerTask(new LinkTimerTask(*this, broker->getTimer())) + timerTask(new LinkTimerTask(*this, broker->getTimer())), + failoverChannel(0) { if (parent != 0 && broker != 0) { @@ -106,15 +168,26 @@ Link::Link(LinkRegistry* _links, startConnectionLH(); } broker->getTimer().add(timerTask); + + stringstream _name; + _name << "qpid.link." << transport << ":" << host << ":" << port; + std::pair<Exchange::shared_ptr, bool> rc = broker->getExchanges().declare(_name.str(), + exchangeTypeName); + failoverExchange = boost::static_pointer_cast<LinkExchange>(rc.first); + assert(failoverExchange); + failoverExchange->setLink(this); } Link::~Link () { - if (state == STATE_OPERATIONAL && connection != 0) - connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); + if (state == STATE_OPERATIONAL && connection != 0) { + closeConnection("closed by management"); + } if (mgmtObject != 0) mgmtObject->resourceDestroy (); + + broker->getExchanges().destroy(failoverExchange->getName()); } void Link::setStateLH (int newState) @@ -180,11 +253,21 @@ void Link::established(Connection* c) void Link::setUrl(const Url& u) { + QPID_LOG(info, "Setting remote broker failover addresses for link '" << getName() << "' to these urls: " << u); Mutex::ScopedLock mutex(lock); url = u; reconnectNext = 0; } + +namespace { + /** invoked when session used to subscribe to remote's amq.failover exchange detaches */ + void sessionDetached(Link *link) { + QPID_LOG(debug, "detached from 'amq.failover' for link: " << link->getName()); + } +} + + void Link::opened() { Mutex::ScopedLock mutex(lock); if (!connection) return; @@ -198,37 +281,74 @@ void Link::opened() { reconnectNext = 0; QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << url); } + + // + // attempt to subscribe to failover exchange for updates from remote + // + + const std::string queueName = "qpid.link." + framing::Uuid(true).str(); + failoverChannel = nextChannel(); + + SessionHandler& sessionHandler = connection->getChannel(failoverChannel); + sessionHandler.setDetachedCallback( boost::bind(&sessionDetached, this) ); + failoverSession = queueName; + sessionHandler.attachAs(failoverSession); + + framing::AMQP_ServerProxy remoteBroker(sessionHandler.out); + + remoteBroker.getQueue().declare(queueName, + "", // alt-exchange + false, // passive + false, // durable + true, // exclusive + true, // auto-delete + FieldTable()); + remoteBroker.getExchange().bind(queueName, + FAILOVER_EXCHANGE, + "", // no key + FieldTable()); + remoteBroker.getMessage().subscribe(queueName, + failoverExchange->getName(), + 1, // implied-accept mode + 0, // pre-acquire mode + false, // exclusive + "", // resume-id + 0, // resume-ttl + FieldTable()); + remoteBroker.getMessage().flow(failoverExchange->getName(), 0, 0xFFFFFFFF); + remoteBroker.getMessage().flow(failoverExchange->getName(), 1, 0xFFFFFFFF); } void Link::closed(int, std::string text) { - Mutex::ScopedLock mutex(lock); - QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text); - - connection = 0; + bool isClosing = false; + { + Mutex::ScopedLock mutex(lock); + QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text); - if (state == STATE_OPERATIONAL) { - stringstream addr; - addr << host << ":" << port; - QPID_LOG(warning, "Inter-broker link disconnected from " << addr.str()); - if (!hideManagement() && agent) - agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); - } + connection = 0; + if (state == STATE_OPERATIONAL) { + stringstream addr; + addr << host << ":" << port; + if (!hideManagement() && agent) + agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); + } - for (Bridges::iterator i = active.begin(); i != active.end(); i++) { - (*i)->closed(); - created.push_back(*i); - } - active.clear(); + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); + created.push_back(*i); + } + active.clear(); - if (state != STATE_FAILED && state != STATE_PASSIVE) - { - setStateLH(STATE_WAITING); - if (!hideManagement()) - mgmtObject->set_lastError (text); + if (state != STATE_FAILED && state != STATE_PASSIVE) + { + setStateLH(STATE_WAITING); + if (!hideManagement()) + mgmtObject->set_lastError (text); + } } - - if (closing) + // Call destroy outside of the lock, don't want to be deleted with lock held. + if (isClosing) destroy(); } @@ -239,10 +359,8 @@ void Link::destroy () { Mutex::ScopedLock mutex(lock); - QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management"); - if (connection) - connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); - connection = 0; + QPID_LOG (info, "Inter-broker link to " << configuredHost << ":" << configuredPort << " removed by management"); + closeConnection("closed by management"); setStateLH(STATE_CLOSED); // Move the bridges to be deleted into a local vector so there is no @@ -263,7 +381,7 @@ void Link::destroy () for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++) (*i)->destroy(); toDelete.clear(); - links->destroy (host, port); + links->destroy (configuredHost, configuredPort); } void Link::add(Bridge::shared_ptr bridge) @@ -311,7 +429,7 @@ void Link::ioThreadProcessing() // check for bridge session errors and recover if (!active.empty()) { Bridges::iterator removed = std::remove_if( - active.begin(), active.end(), !boost::bind(&Bridge::isSessionReady, _1)); + active.begin(), active.end(), boost::bind(&Bridge::isDetached, _1)); for (Bridges::iterator i = removed; i != active.end(); ++i) { Bridge::shared_ptr bridge = *i; bridge->closed(); @@ -398,14 +516,14 @@ bool Link::hideManagement() const { uint Link::nextChannel() { Mutex::ScopedLock mutex(lock); - + if (channelCounter >= framing::CHANNEL_MAX) + channelCounter = 1; return channelCounter++; } void Link::notifyConnectionForced(const string text) { Mutex::ScopedLock mutex(lock); - setStateLH(STATE_FAILED); if (!hideManagement()) mgmtObject->set_lastError(text); @@ -418,7 +536,7 @@ void Link::setPersistenceId(uint64_t id) const const string& Link::getName() const { - return host; + return configuredHost; } Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer) @@ -444,9 +562,9 @@ Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer) void Link::encode(Buffer& buffer) const { buffer.putShortString(string("link")); - buffer.putShortString(host); - buffer.putShort(port); - buffer.putShortString(transport); + buffer.putShortString(configuredHost); + buffer.putShort(configuredPort); + buffer.putShortString(configuredTransport); buffer.putOctet(durable ? 1 : 0); buffer.putShortString(authMechanism); buffer.putShortString(username); @@ -455,10 +573,10 @@ void Link::encode(Buffer& buffer) const uint32_t Link::encodedSize() const { - return host.size() + 1 // short-string (host) + return configuredHost.size() + 1 // short-string (host) + 5 // short-string ("link") + 2 // port - + transport.size() + 1 // short-string(transport) + + configuredTransport.size() + 1 // short-string(transport) + 1 // durable + authMechanism.size() + 1 + username.size() + 1 @@ -513,7 +631,7 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& te } std::pair<Bridge::shared_ptr, bool> result = - links->declare (host, port, iargs.i_durable, iargs.i_src, + links->declare (configuredHost, configuredPort, iargs.i_durable, iargs.i_src, iargs.i_dest, iargs.i_key, iargs.i_srcIsQueue, iargs.i_srcIsLocal, iargs.i_tag, iargs.i_excludes, iargs.i_dynamic, iargs.i_sync); @@ -542,4 +660,63 @@ void Link::setPassive(bool passive) } } + +/** utility to clean up connection resources correctly */ +void Link::closeConnection( const std::string& reason) +{ + if (connection != 0) { + // cancel our subscription to the failover exchange + SessionHandler& sessionHandler = connection->getChannel(failoverChannel); + if (sessionHandler.getSession()) { + framing::AMQP_ServerProxy remoteBroker(sessionHandler.out); + remoteBroker.getMessage().cancel(failoverExchange->getName()); + remoteBroker.getSession().detach(failoverSession); + } + connection->close(CLOSE_CODE_CONNECTION_FORCED, reason); + connection = 0; + } +} + +/** returns the current remote's address, and connection state */ +bool Link::getRemoteAddress(qpid::Address& addr) const +{ + addr.protocol = transport; + addr.host = host; + addr.port = port; + + return state == STATE_OPERATIONAL; +} + + +// FieldTable keys for internal state data +namespace { + const std::string FAILOVER_ADDRESSES("failover-addresses"); + const std::string FAILOVER_INDEX("failover-index"); +} + +void Link::getState(framing::FieldTable& state) const +{ + state.clear(); + Mutex::ScopedLock mutex(lock); + if (!url.empty()) { + state.setString(FAILOVER_ADDRESSES, url.str()); + state.setInt(FAILOVER_INDEX, reconnectNext); + } +} + +void Link::setState(const framing::FieldTable& state) +{ + Mutex::ScopedLock mutex(lock); + if (state.isSet(FAILOVER_ADDRESSES)) { + Url failovers(state.getAsString(FAILOVER_ADDRESSES)); + setUrl(failovers); + } + if (state.isSet(FAILOVER_INDEX)) { + reconnectNext = state.getAsInt(FAILOVER_INDEX); + } +} + + +const std::string Link::exchangeTypeName("qpid.LinkExchange"); + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h index 4085c3bfcf..a97fa48664 100644 --- a/cpp/src/qpid/broker/Link.h +++ b/cpp/src/qpid/broker/Link.h @@ -24,9 +24,11 @@ #include <boost/shared_ptr.hpp> #include "qpid/Url.h" +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/MessageStore.h" #include "qpid/broker/PersistableConfig.h" #include "qpid/broker/Bridge.h" +#include "qpid/broker/BrokerImportExport.h" #include "qpid/sys/Mutex.h" #include "qpid/framing/FieldTable.h" #include "qpid/management/Manageable.h" @@ -45,15 +47,23 @@ namespace broker { class LinkRegistry; class Broker; class Connection; +class LinkExchange; class Link : public PersistableConfig, public management::Manageable { private: - sys::Mutex lock; + mutable sys::Mutex lock; LinkRegistry* links; MessageStore* store; - std::string host; - uint16_t port; - std::string transport; + + // these remain constant across failover - used to identify this link + const std::string configuredTransport; + const std::string configuredHost; + const uint16_t configuredPort; + // these reflect the current address of remote - will change during failover + std::string host; + uint16_t port; + std::string transport; + bool durable; std::string authMechanism; std::string username; @@ -75,8 +85,10 @@ class Link : public PersistableConfig, public management::Manageable { uint channelCounter; Connection* connection; management::ManagementAgent* agent; - boost::intrusive_ptr<sys::TimerTask> timerTask; + boost::shared_ptr<broker::LinkExchange> failoverExchange; // subscribed to remote's amq.failover exchange + uint failoverChannel; + std::string failoverSession; static const int STATE_WAITING = 1; static const int STATE_CONNECTING = 2; @@ -94,6 +106,14 @@ class Link : public PersistableConfig, public management::Manageable { bool tryFailoverLH(); // Called during maintenance visit bool hideManagement() const; + void established(Connection*); // Called when connection is create + void opened(); // Called when connection is open (after create) + void closed(int, std::string); // Called when connection goes away + void reconnectLH(const Address&); //called by LinkRegistry + void closeConnection(const std::string& reason); + + friend class LinkRegistry; // to call established, opened, closed + public: typedef boost::shared_ptr<Link> shared_ptr; @@ -110,22 +130,25 @@ class Link : public PersistableConfig, public management::Manageable { management::Manageable* parent = 0); virtual ~Link(); - std::string getHost() { return host; } - uint16_t getPort() { return port; } - std::string getTransport() { return transport; } + /** these return the *configured* transport/host/port, which does not change over the + lifetime of the Link */ + std::string getHost() const { return configuredHost; } + uint16_t getPort() const { return configuredPort; } + std::string getTransport() const { return configuredTransport; } + + /** returns the current address of the remote, which may be different from the + configured transport/host/port due to failover. Returns true if connection is + active */ + bool getRemoteAddress(qpid::Address& addr) const; bool isDurable() { return durable; } void maintenanceVisit (); uint nextChannel(); void add(Bridge::shared_ptr); void cancel(Bridge::shared_ptr); - void setUrl(const Url&); // Set URL for reconnection. - void established(Connection*); // Called when connection is create - void opened(); // Called when connection is open (after create) - void closed(int, std::string); // Called when connection goes away - void reconnectLH(const Address&); //called by LinkRegistry - void close(); // Close the link from within the broker. + QPID_BROKER_EXTERN void setUrl(const Url&); // Set URL for reconnection. + QPID_BROKER_EXTERN void close(); // Close the link from within the broker. std::string getAuthMechanism() { return authMechanism; } std::string getUsername() { return username; } @@ -148,6 +171,13 @@ class Link : public PersistableConfig, public management::Manageable { management::ManagementObject* GetManagementObject(void) const; management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&); + // manage the exchange owned by this link + static const std::string exchangeTypeName; + static boost::shared_ptr<Exchange> linkExchangeFactory(const std::string& name); + + // replicate internal state of this Link for clustering + void getState(framing::FieldTable& state) const; + void setState(const framing::FieldTable& state); }; } } diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp index bb602bb953..d89f220d1b 100644 --- a/cpp/src/qpid/broker/LinkRegistry.cpp +++ b/cpp/src/qpid/broker/LinkRegistry.cpp @@ -25,7 +25,9 @@ #include <iostream> #include <boost/format.hpp> -using namespace qpid::broker; +namespace qpid { +namespace broker { + using namespace qpid::sys; using std::string; using std::pair; @@ -45,16 +47,15 @@ LinkRegistry::LinkRegistry () : { } -namespace { -struct ConnectionObserverImpl : public ConnectionObserver { +class LinkRegistryConnectionObserver : public ConnectionObserver { LinkRegistry& links; - ConnectionObserverImpl(LinkRegistry& l) : links(l) {} + public: + LinkRegistryConnectionObserver(LinkRegistry& l) : links(l) {} void connection(Connection& c) { links.notifyConnection(c.getMgmtId(), &c); } void opened(Connection& c) { links.notifyOpened(c.getMgmtId()); } void closed(Connection& c) { links.notifyClosed(c.getMgmtId()); } void forced(Connection& c, const string& text) { links.notifyConnectionForced(c.getMgmtId(), text); } }; -} LinkRegistry::LinkRegistry (Broker* _broker) : broker(_broker), @@ -62,7 +63,7 @@ LinkRegistry::LinkRegistry (Broker* _broker) : realm(broker->getOptions().realm) { broker->getConnectionObservers().add( - boost::shared_ptr<ConnectionObserver>(new ConnectionObserverImpl(*this))); + boost::shared_ptr<ConnectionObserver>(new LinkRegistryConnectionObserver(*this))); } LinkRegistry::~LinkRegistry() {} @@ -298,22 +299,29 @@ std::string LinkRegistry::getUsername(const std::string& key) return link->getUsername(); } +/** note: returns the current remote host (may be different from the host originally + configured for the Link due to failover) */ std::string LinkRegistry::getHost(const std::string& key) { - Link::shared_ptr link = findLink(key); - if (!link) - return string(); + Link::shared_ptr link = findLink(key); + if (!link) + return string(); - return link->getHost(); + qpid::Address addr; + link->getRemoteAddress(addr); + return addr.host; } +/** returns the current remote port (ditto above) */ uint16_t LinkRegistry::getPort(const std::string& key) { Link::shared_ptr link = findLink(key); if (!link) return 0; - return link->getPort(); + qpid::Address addr; + link->getRemoteAddress(addr); + return addr.port; } std::string LinkRegistry::getPassword(const std::string& key) @@ -368,3 +376,4 @@ void LinkRegistry::eachBridge(boost::function<void(boost::shared_ptr<Bridge>)> f for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) f(i->second); } +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h index 753f6bfe9e..8e9d2f4b0d 100644 --- a/cpp/src/qpid/broker/LinkRegistry.h +++ b/cpp/src/qpid/broker/LinkRegistry.h @@ -23,6 +23,7 @@ */ #include <map> +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/Bridge.h" #include "qpid/broker/MessageStore.h" #include "qpid/Address.h" @@ -56,43 +57,50 @@ namespace broker { static std::string createKey(const Address& address); static std::string createKey(const std::string& host, uint16_t port); + // Methods called by the connection observer. + void notifyConnection (const std::string& key, Connection* c); + void notifyOpened (const std::string& key); + void notifyClosed (const std::string& key); + void notifyConnectionForced (const std::string& key, const std::string& text); + friend class LinkRegistryConnectionObserver; + public: - LinkRegistry (); // Only used in store tests - LinkRegistry (Broker* _broker); - ~LinkRegistry(); - - std::pair<boost::shared_ptr<Link>, bool> - declare(const std::string& host, - uint16_t port, - const std::string& transport, - bool durable, - const std::string& authMechanism, - const std::string& username, - const std::string& password); - - std::pair<Bridge::shared_ptr, bool> - declare(const std::string& host, - uint16_t port, - bool durable, - const std::string& src, - const std::string& dest, - const std::string& key, - bool isQueue, - bool isLocal, - const std::string& id, - const std::string& excludes, - bool dynamic, - uint16_t sync, - Bridge::InitializeCallback=0 - ); - - void destroy(const std::string& host, const uint16_t port); - - void destroy(const std::string& host, - const uint16_t port, - const std::string& src, - const std::string& dest, - const std::string& key); + QPID_BROKER_EXTERN LinkRegistry (); // Only used in store tests + QPID_BROKER_EXTERN LinkRegistry (Broker* _broker); + QPID_BROKER_EXTERN ~LinkRegistry(); + + QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Link>, bool> + declare(const std::string& host, + uint16_t port, + const std::string& transport, + bool durable, + const std::string& authMechanism, + const std::string& username, + const std::string& password); + + QPID_BROKER_EXTERN std::pair<Bridge::shared_ptr, bool> + declare(const std::string& host, + uint16_t port, + bool durable, + const std::string& src, + const std::string& dest, + const std::string& key, + bool isQueue, + bool isLocal, + const std::string& id, + const std::string& excludes, + bool dynamic, + uint16_t sync, + Bridge::InitializeCallback=0 + ); + + QPID_BROKER_EXTERN void destroy(const std::string& host, const uint16_t port); + + QPID_BROKER_EXTERN void destroy(const std::string& host, + const uint16_t port, + const std::string& src, + const std::string& dest, + const std::string& key); /** * Register the manageable parent for declared queues @@ -102,24 +110,20 @@ namespace broker { /** * Set the store to use. May only be called once. */ - void setStore (MessageStore*); + QPID_BROKER_EXTERN void setStore (MessageStore*); /** * Return the message store used. */ - MessageStore* getStore() const; + QPID_BROKER_EXTERN MessageStore* getStore() const; - void notifyConnection (const std::string& key, Connection* c); - void notifyOpened (const std::string& key); - void notifyClosed (const std::string& key); - void notifyConnectionForced (const std::string& key, const std::string& text); - std::string getAuthMechanism (const std::string& key); - std::string getAuthCredentials (const std::string& key); - std::string getAuthIdentity (const std::string& key); - std::string getUsername (const std::string& key); - std::string getPassword (const std::string& key); - std::string getHost (const std::string& key); - uint16_t getPort (const std::string& key); + QPID_BROKER_EXTERN std::string getAuthMechanism (const std::string& key); + QPID_BROKER_EXTERN std::string getAuthCredentials (const std::string& key); + QPID_BROKER_EXTERN std::string getAuthIdentity (const std::string& key); + QPID_BROKER_EXTERN std::string getUsername (const std::string& key); + QPID_BROKER_EXTERN std::string getPassword (const std::string& key); + QPID_BROKER_EXTERN std::string getHost (const std::string& key); + QPID_BROKER_EXTERN uint16_t getPort (const std::string& key); /** * Called by links failing over to new address @@ -132,13 +136,13 @@ namespace broker { * updated but links won't actually establish connections and * bridges won't therefore pull or push any messages. */ - void setPassive(bool); - bool isPassive() { return passive; } + QPID_BROKER_EXTERN void setPassive(bool); + QPID_BROKER_EXTERN bool isPassive() { return passive; } /** Iterate over each link in the registry. Used for cluster updates. */ - void eachLink(boost::function<void(boost::shared_ptr<Link>)> f); + QPID_BROKER_EXTERN void eachLink(boost::function<void(boost::shared_ptr<Link>)> f); /** Iterate over each bridge in the registry. Used for cluster updates. */ - void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f); + QPID_BROKER_EXTERN void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f); }; } } diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp index ae4503328a..40dfba39f4 100644 --- a/cpp/src/qpid/broker/Message.cpp +++ b/cpp/src/qpid/broker/Message.cpp @@ -131,6 +131,7 @@ uint32_t Message::getRequiredCredit() void Message::encode(framing::Buffer& buffer) const { + sys::Mutex::ScopedLock l(lock); //encode method and header frames EncodeFrame f1(buffer); frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>()); @@ -142,6 +143,7 @@ void Message::encode(framing::Buffer& buffer) const void Message::encodeContent(framing::Buffer& buffer) const { + sys::Mutex::ScopedLock l(lock); //encode the payload of each content frame EncodeBody f2(buffer); frames.map_if(f2, TypeFilter<CONTENT_BODY>()); @@ -154,11 +156,13 @@ uint32_t Message::encodedSize() const uint32_t Message::encodedContentSize() const { + sys::Mutex::ScopedLock l(lock); return frames.getContentSize(); } uint32_t Message::encodedHeaderSize() const { + sys::Mutex::ScopedLock l(lock); // prevent modifications while computing size //add up the size for all method and header frames in the frameset SumFrameSize sum; frames.map_if(sum, TypeFilter2<METHOD_BODY, HEADER_BODY>()); @@ -218,8 +222,9 @@ void Message::releaseContent() store->stage(pmsg); staged = true; } - //ensure required credit is cached before content frames are released + //ensure required credit and size is cached before content frames are released getRequiredCredit(); + contentSize(); //remove any content frames from the frameset frames.remove(TypeFilter<CONTENT_BODY>()); setContentReleased(); @@ -354,6 +359,7 @@ public: AMQHeaderBody* Message::getHeaderBody() { + // expects lock to be held if (copyHeaderOnWrite) { CloneHeaderBody f; frames.map_if(f, TypeFilter<HEADER_BODY>()); diff --git a/cpp/src/qpid/broker/MessageDeque.cpp b/cpp/src/qpid/broker/MessageDeque.cpp index 709d99876b..f70c996975 100644 --- a/cpp/src/qpid/broker/MessageDeque.cpp +++ b/cpp/src/qpid/broker/MessageDeque.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/MessageDeque.h" #include "qpid/broker/QueuedMessage.h" #include "qpid/log/Statement.h" +#include "assert.h" namespace qpid { namespace broker { @@ -39,7 +40,7 @@ size_t MessageDeque::index(const framing::SequenceNumber& position) bool MessageDeque::deleted(const QueuedMessage& m) { size_t i = index(m.position); - if (i < messages.size()) { + if (i < messages.size() && messages[i].status != QueuedMessage::DELETED) { messages[i].status = QueuedMessage::DELETED; clean(); return true; @@ -53,7 +54,7 @@ size_t MessageDeque::size() return available; } -void MessageDeque::release(const QueuedMessage& message) +QueuedMessage* MessageDeque::releasePtr(const QueuedMessage& message) { size_t i = index(message.position); if (i < messages.size()) { @@ -62,12 +63,17 @@ void MessageDeque::release(const QueuedMessage& message) if (head > i) head = i; m.status = QueuedMessage::AVAILABLE; ++available; + return &messages[i]; } } else { + assert(0); QPID_LOG(error, "Failed to release message at " << message.position << " " << message.payload->getFrames().getContent() << "; no such message (index=" << i << ", size=" << messages.size() << ")"); } + return 0; } +void MessageDeque::release(const QueuedMessage& message) { releasePtr(message); } + bool MessageDeque::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { if (position < messages.front().position) return false; @@ -129,8 +135,7 @@ QueuedMessage padding(uint32_t pos) { } } // namespace -bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) -{ +QueuedMessage* MessageDeque::pushPtr(const QueuedMessage& added) { //add padding to prevent gaps in sequence, which break the index //calculation (needed for queue replication) while (messages.size() && (added.position - messages.back().position) > 1) @@ -139,7 +144,12 @@ bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed* messages.back().status = QueuedMessage::AVAILABLE; if (head >= messages.size()) head = messages.size() - 1; ++available; - return false;//adding a message never causes one to be removed for deque + return &messages.back(); +} + +bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) { + pushPtr(added); + return false; // adding a message never causes one to be removed for deque } void MessageDeque::updateAcquired(const QueuedMessage& acquired) diff --git a/cpp/src/qpid/broker/MessageDeque.h b/cpp/src/qpid/broker/MessageDeque.h index bb5943b09b..9b53716d4e 100644 --- a/cpp/src/qpid/broker/MessageDeque.h +++ b/cpp/src/qpid/broker/MessageDeque.h @@ -48,6 +48,12 @@ class MessageDeque : public Messages void foreach(Functor); void removeIf(Predicate); + // For use by other Messages implementations that use MessageDeque as a FIFO index + // and keep pointers to its elements in their own indexing strctures. + void clean(); + QueuedMessage* releasePtr(const QueuedMessage&); + QueuedMessage* pushPtr(const QueuedMessage& added); + private: typedef std::deque<QueuedMessage> Deque; Deque messages; @@ -55,7 +61,6 @@ class MessageDeque : public Messages size_t head; size_t index(const framing::SequenceNumber&); - void clean(); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/MessageGroupManager.cpp b/cpp/src/qpid/broker/MessageGroupManager.cpp index 5f450cd556..15cd56a676 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.cpp +++ b/cpp/src/qpid/broker/MessageGroupManager.cpp @@ -19,11 +19,13 @@ * */ +#include "qpid/broker/MessageGroupManager.h" + +#include "qpid/broker/Queue.h" #include "qpid/framing/FieldTable.h" -#include "qpid/types/Variant.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/MessageGroupManager.h" +#include "qpid/types/Variant.h" using namespace qpid::broker; @@ -43,9 +45,18 @@ const std::string MessageGroupManager::qpidSharedGroup("qpid.shared_msg_group"); const std::string MessageGroupManager::qpidMessageGroupTimestamp("qpid.group_timestamp"); +/** return an iterator to the message at position, or members.end() if not found */ +MessageGroupManager::GroupState::MessageFifo::iterator +MessageGroupManager::GroupState::findMsg(const qpid::framing::SequenceNumber &position) +{ + MessageState mState(position); + MessageFifo::iterator found = std::lower_bound(members.begin(), members.end(), mState); + return (found->position == position) ? found : members.end(); +} + void MessageGroupManager::unFree( const GroupState& state ) { - GroupFifo::iterator pos = freeGroups.find( state.members.front() ); + GroupFifo::iterator pos = freeGroups.find( state.members.front().position ); assert( pos != freeGroups.end() && pos->second == &state ); freeGroups.erase( pos ); } @@ -60,8 +71,8 @@ void MessageGroupManager::disown( GroupState& state ) { state.owner.clear(); assert(state.members.size()); - assert(freeGroups.find(state.members.front()) == freeGroups.end()); - freeGroups[state.members.front()] = &state; + assert(freeGroups.find(state.members.front().position) == freeGroups.end()); + freeGroups[state.members.front().position] = &state; } MessageGroupManager::GroupState& MessageGroupManager::findGroup( const QueuedMessage& qm ) @@ -106,7 +117,8 @@ void MessageGroupManager::enqueued( const QueuedMessage& qm ) // @todo KAG optimization - store reference to group state in QueuedMessage // issue: const-ness?? GroupState& state = findGroup(qm); - state.members.push_back(qm.position); + GroupState::MessageState mState(qm.position); + state.members.push_back(mState); uint32_t total = state.members.size(); QPID_LOG( trace, "group queue " << qName << ": added message to group id=" << state.group << " total=" << total ); @@ -123,7 +135,9 @@ void MessageGroupManager::acquired( const QueuedMessage& qm ) // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? GroupState& state = findGroup(qm); - assert(state.members.size()); // there are msgs present + GroupState::MessageFifo::iterator m = state.findMsg(qm.position); + assert(m != state.members.end()); + m->acquired = true; state.acquired += 1; QPID_LOG( trace, "group queue " << qName << ": acquired message in group id=" << state.group << " acquired=" << state.acquired ); @@ -137,6 +151,9 @@ void MessageGroupManager::requeued( const QueuedMessage& qm ) GroupState& state = findGroup(qm); assert( state.acquired != 0 ); state.acquired -= 1; + GroupState::MessageFifo::iterator m = state.findMsg(qm.position); + assert(m != state.members.end()); + m->acquired = false; if (state.acquired == 0 && state.owned()) { QPID_LOG( trace, "group queue " << qName << ": consumer name=" << state.owner << " released group id=" << state.group); @@ -152,13 +169,17 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? GroupState& state = findGroup(qm); - assert( state.members.size() != 0 ); - assert( state.acquired != 0 ); - state.acquired -= 1; + GroupState::MessageFifo::iterator m = state.findMsg(qm.position); + assert(m != state.members.end()); + if (m->acquired) { + assert( state.acquired != 0 ); + state.acquired -= 1; + } - // likely to be at or near begin() if dequeued in order + // special case if qm is first (oldest) message in the group: + // may need to re-insert it back on the freeGroups list, as the index will change bool reFreeNeeded = false; - if (state.members.front() == qm.position) { + if (m == state.members.begin()) { if (!state.owned()) { // will be on the freeGroups list if mgmt is dequeueing rather than a consumer! // if on freelist, it is indexed by first member, which is about to be removed! @@ -167,15 +188,7 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) } state.members.pop_front(); } else { - GroupState::PositionFifo::iterator pos = state.members.begin() + 1; - GroupState::PositionFifo::iterator end = state.members.end(); - while (pos != end) { - if (*pos == qm.position) { - state.members.erase(pos); - break; - } - ++pos; - } + state.members.erase(m); } uint32_t total = state.members.size(); @@ -220,11 +233,11 @@ bool MessageGroupManager::nextConsumableMessage( Consumer::shared_ptr& c, Queued GroupState& group = findGroup(next); if (!group.owned()) { //TODO: make acquire more efficient when we already have the message in question - if (group.members.front() == next.position && messages.acquire(next.position, next)) { // only take from head! + if (group.members.front().position == next.position && messages.acquire(next.position, next)) { // only take from head! return true; } QPID_LOG(debug, "Skipping " << next.position << " since group " << group.group - << "'s head message still pending. pos=" << group.members.front()); + << "'s head message still pending. pos=" << group.members.front().position); } else if (group.owner == c->getName() && messages.acquire(next.position, next)) { return true; } @@ -284,7 +297,7 @@ void MessageGroupManager::query(qpid::types::Variant::Map& status) const info[GROUP_TIMESTAMP] = 0; if (g->second.members.size() != 0) { QueuedMessage qm; - if (messages.find(g->second.members.front(), qm) && + if (messages.find(g->second.members.front().position, qm) && qm.payload && qm.payload->hasProperties<framing::DeliveryProperties>()) { info[GROUP_TIMESTAMP] = qm.payload->getProperties<framing::DeliveryProperties>()->getTimestamp(); @@ -353,6 +366,7 @@ namespace { const std::string GROUP_OWNER("owner"); const std::string GROUP_ACQUIRED_CT("acquired-ct"); const std::string GROUP_POSITIONS("positions"); + const std::string GROUP_ACQUIRED_MSGS("acquired-msgs"); const std::string GROUP_STATE("group-state"); } @@ -371,10 +385,14 @@ void MessageGroupManager::getState(qpid::framing::FieldTable& state ) const group.setString(GROUP_OWNER, g->second.owner); group.setInt(GROUP_ACQUIRED_CT, g->second.acquired); framing::Array positions(TYPE_CODE_UINT32); - for (GroupState::PositionFifo::const_iterator p = g->second.members.begin(); - p != g->second.members.end(); ++p) - positions.push_back(framing::Array::ValuePtr(new IntegerValue( *p ))); + framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN); + for (GroupState::MessageFifo::const_iterator p = g->second.members.begin(); + p != g->second.members.end(); ++p) { + positions.push_back(framing::Array::ValuePtr(new IntegerValue( p->position ))); + acquiredMsgs.push_back(framing::Array::ValuePtr(new BoolValue( p->acquired ))); + } group.setArray(GROUP_POSITIONS, positions); + group.setArray(GROUP_ACQUIRED_MSGS, acquiredMsgs); groupState.push_back(framing::Array::ValuePtr(new FieldTableValue(group))); } state.setArray(GROUP_STATE, groupState); @@ -425,13 +443,25 @@ void MessageGroupManager::setState(const qpid::framing::FieldTable& state) qName << "\": position encoding error!"); return; } + framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN); + ok = group.getArray(GROUP_ACQUIRED_MSGS, acquiredMsgs); + if (!ok || positions.count() != acquiredMsgs.count()) { + QPID_LOG(error, "Invalid message group state information for queue \"" << + qName << "\": acquired flag encoding error!"); + return; + } + + Array::const_iterator a = acquiredMsgs.begin(); + for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) { + GroupState::MessageState mState((*p)->getIntegerValue<uint32_t, 4>()); + mState.acquired = (*a++)->getIntegerValue<bool>(); + state.members.push_back(mState); + } - for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) - state.members.push_back((*p)->getIntegerValue<uint32_t, 4>()); messageGroups[state.group] = state; if (!state.owned()) { assert(state.members.size()); - freeGroups[state.members.front()] = &messageGroups[state.group]; + freeGroups[state.members.front().position] = &messageGroups[state.group]; } } diff --git a/cpp/src/qpid/broker/MessageGroupManager.h b/cpp/src/qpid/broker/MessageGroupManager.h index f4bffc4760..2dd97ea2ff 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.h +++ b/cpp/src/qpid/broker/MessageGroupManager.h @@ -28,11 +28,14 @@ #include "qpid/broker/MessageDistributor.h" #include "qpid/sys/unordered_map.h" +#include <deque> + namespace qpid { namespace broker { class QueueObserver; class MessageDistributor; +class Messages; class MessageGroupManager : public StatefulQueueObserver, public MessageDistributor { @@ -45,19 +48,29 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu struct GroupState { // note: update getState()/setState() when changing this object's state implementation - typedef std::deque<framing::SequenceNumber> PositionFifo; + + // track which messages are in this group, and if they have been acquired + struct MessageState { + qpid::framing::SequenceNumber position; + bool acquired; + MessageState() : acquired(false) {} + MessageState(const qpid::framing::SequenceNumber& p) : position(p), acquired(false) {} + bool operator<(const MessageState& b) const { return position < b.position; } + }; + typedef std::deque<MessageState> MessageFifo; std::string group; // group identifier std::string owner; // consumer with outstanding acquired messages uint32_t acquired; // count of outstanding acquired messages - PositionFifo members; // msgs belonging to this group + MessageFifo members; // msgs belonging to this group, in enqueue order GroupState() : acquired(0) {} bool owned() const {return !owner.empty();} + MessageFifo::iterator findMsg(const qpid::framing::SequenceNumber &); }; typedef sys::unordered_map<std::string, struct GroupState> GroupMap; - typedef std::map<framing::SequenceNumber, struct GroupState *> GroupFifo; + typedef std::map<qpid::framing::SequenceNumber, struct GroupState *> GroupFifo; GroupMap messageGroups; // index: group name GroupFifo freeGroups; // ordered by oldest free msg diff --git a/cpp/src/qpid/broker/MessageMap.cpp b/cpp/src/qpid/broker/MessageMap.cpp index 048df45434..9b164d4e5c 100644 --- a/cpp/src/qpid/broker/MessageMap.cpp +++ b/cpp/src/qpid/broker/MessageMap.cpp @@ -20,6 +20,7 @@ */ #include "qpid/broker/MessageMap.h" #include "qpid/broker/QueuedMessage.h" +#include "qpid/log/Statement.h" namespace qpid { namespace broker { @@ -27,7 +28,16 @@ namespace { const std::string EMPTY; } -bool MessageMap::deleted(const QueuedMessage&) { return true; } +bool MessageMap::deleted(const QueuedMessage& message) +{ + Ordering::iterator i = messages.find(message.position); + if (i != messages.end()) { + erase(i); + return true; + } else { + return false; + } +} std::string MessageMap::getKey(const QueuedMessage& message) { @@ -38,30 +48,32 @@ std::string MessageMap::getKey(const QueuedMessage& message) size_t MessageMap::size() { - return messages.size(); + size_t count(0); + for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { + if (i->second.status == QueuedMessage::AVAILABLE) ++count; + } + return count; } bool MessageMap::empty() { - return messages.empty(); + return size() == 0;//TODO: more efficient implementation } void MessageMap::release(const QueuedMessage& message) { - std::string key = getKey(message); - Index::iterator i = index.find(key); - if (i == index.end()) { - index[key] = message; - messages[message.position] = message; - } //else message has already been replaced + Ordering::iterator i = messages.find(message.position); + if (i != messages.end() && i->second.status == QueuedMessage::ACQUIRED) { + i->second.status = QueuedMessage::AVAILABLE; + } } bool MessageMap::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { Ordering::iterator i = messages.find(position); - if (i != messages.end()) { + if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) { + i->second.status = QueuedMessage::ACQUIRED; message = i->second; - erase(i); return true; } else { return false; @@ -71,7 +83,7 @@ bool MessageMap::acquire(const framing::SequenceNumber& position, QueuedMessage& bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& message) { Ordering::iterator i = messages.find(position); - if (i != messages.end()) { + if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) { message = i->second; return true; } else { @@ -79,10 +91,10 @@ bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& me } } -bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool) +bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) { Ordering::iterator i = messages.lower_bound(position+1); - if (i != messages.end()) { + if (i != messages.end() && (i->second.status == QueuedMessage::AVAILABLE || (!unacquired && i->second.status == QueuedMessage::ACQUIRED))) { message = i->second; return true; } else { @@ -92,14 +104,14 @@ bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& bool MessageMap::consume(QueuedMessage& message) { - Ordering::iterator i = messages.begin(); - if (i != messages.end()) { - message = i->second; - erase(i); - return true; - } else { - return false; + for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { + if (i->second.status == QueuedMessage::AVAILABLE) { + i->second.status = QueuedMessage::ACQUIRED; + message = i->second; + return true; + } } + return false; } const QueuedMessage& MessageMap::replace(const QueuedMessage& original, const QueuedMessage& update) @@ -115,12 +127,17 @@ bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed) if (result.second) { //there was no previous message for this key; nothing needs to //be removed, just add the message into its correct position - messages[added.position] = added; + QueuedMessage& a = messages[added.position]; + a = added; + a.status = QueuedMessage::AVAILABLE; + QPID_LOG(debug, "Added message at " << a.position); return false; } else { //there is already a message with that key which needs to be replaced removed = result.first->second; result.first->second = replace(result.first->second, added); + result.first->second.status = QueuedMessage::AVAILABLE; + QPID_LOG(debug, "Displaced message at " << removed.position << " with " << result.first->second.position << ": " << result.first->first); return true; } } @@ -128,15 +145,24 @@ bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed) void MessageMap::foreach(Functor f) { for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { - f(i->second); + if (i->second.status == QueuedMessage::AVAILABLE) f(i->second); } } void MessageMap::removeIf(Predicate p) { - for (Ordering::iterator i = messages.begin(); i != messages.end(); i++) { - if (p(i->second)) { - erase(i); + for (Ordering::iterator i = messages.begin(); i != messages.end();) { + if (i->second.status == QueuedMessage::AVAILABLE && p(i->second)) { + index.erase(getKey(i->second)); + //Note: Removing from messages means that the subsequent + //call to deleted() for the same message will return + //false. At present that is not a problem. If this were + //changed to hold onto the message until dequeued + //(e.g. with REMOVED state), then the erase() below would + //need to take that into account. + messages.erase(i++); + } else { + ++i; } } } diff --git a/cpp/src/qpid/broker/MessageMap.h b/cpp/src/qpid/broker/MessageMap.h index d1b8217f9b..a668450250 100644 --- a/cpp/src/qpid/broker/MessageMap.h +++ b/cpp/src/qpid/broker/MessageMap.h @@ -43,7 +43,7 @@ class MessageMap : public Messages size_t size(); bool empty(); - bool deleted(const QueuedMessage&); + virtual bool deleted(const QueuedMessage&); void release(const QueuedMessage&); virtual bool acquire(const framing::SequenceNumber&, QueuedMessage&); bool find(const framing::SequenceNumber&, QueuedMessage&); diff --git a/cpp/src/qpid/broker/PriorityQueue.cpp b/cpp/src/qpid/broker/PriorityQueue.cpp index d807ef22b1..ab5ec7235a 100644 --- a/cpp/src/qpid/broker/PriorityQueue.cpp +++ b/cpp/src/qpid/broker/PriorityQueue.cpp @@ -3,13 +3,13 @@ * 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 + * regarding copyright ownersip. 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 @@ -22,96 +22,87 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/QueuedMessage.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" #include <cmath> namespace qpid { namespace broker { -PriorityQueue::PriorityQueue(int l) : +PriorityQueue::PriorityQueue(int l) : levels(l), messages(levels, Deque()), frontLevel(0), haveFront(false), cached(false) {} -bool PriorityQueue::deleted(const QueuedMessage&) { return true; } +bool PriorityQueue::deleted(const QueuedMessage& qm) { + bool deleted = fifo.deleted(qm); + if (deleted) erase(qm); + return deleted; +} size_t PriorityQueue::size() { - size_t total(0); - for (int i = 0; i < levels; ++i) { - total += messages[i].size(); - } - return total; + return fifo.size(); +} + +namespace { +bool before(QueuedMessage* a, QueuedMessage* b) { return *a < *b; } } void PriorityQueue::release(const QueuedMessage& message) { - uint p = getPriorityLevel(message); - messages[p].insert(lower_bound(messages[p].begin(), messages[p].end(), message), message); - clearCache(); + QueuedMessage* qm = fifo.releasePtr(message); + if (qm) { + uint p = getPriorityLevel(message); + messages[p].insert( + lower_bound(messages[p].begin(), messages[p].end(), qm, before), qm); + clearCache(); + } } -bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message, bool remove) -{ - QueuedMessage comp; - comp.position = position; - for (int i = 0; i < levels; ++i) { - if (!messages[i].empty()) { - unsigned long diff = position.getValue() - messages[i].front().position.getValue(); - long maxEnd = diff < messages[i].size() ? diff : messages[i].size(); - Deque::iterator l = lower_bound(messages[i].begin(),messages[i].begin()+maxEnd,comp); - if (l != messages[i].end() && l->position == position) { - message = *l; - if (remove) { - messages[i].erase(l); - clearCache(); - } - return true; - } + +void PriorityQueue::erase(const QueuedMessage& qm) { + size_t i = getPriorityLevel(qm); + if (!messages[i].empty()) { + long diff = qm.position.getValue() - messages[i].front()->position.getValue(); + if (diff < 0) return; + long maxEnd = std::min(size_t(diff), messages[i].size()); + QueuedMessage mutableQm = qm; // need non-const qm for lower_bound + Deque::iterator l = + lower_bound(messages[i].begin(),messages[i].begin()+maxEnd, &mutableQm, before); + if (l != messages[i].end() && (*l)->position == qm.position) { + messages[i].erase(l); + clearCache(); + return; } } - return false; } bool PriorityQueue::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { - return find(position, message, true); + bool acquired = fifo.acquire(position, message); + if (acquired) erase(message); // No longer available + return acquired; } bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message) { - return find(position, message, false); + return fifo.find(position, message); } -bool PriorityQueue::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool) +bool PriorityQueue::browse( + const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) { - QueuedMessage match; - match.position = position+1; - Deque::iterator lowest; - bool found = false; - for (int i = 0; i < levels; ++i) { - Deque::iterator m = lower_bound(messages[i].begin(), messages[i].end(), match); - if (m != messages[i].end()) { - if (m->position == match.position) { - message = *m; - return true; - } else if (!found || m->position < lowest->position) { - lowest = m; - found = true; - } - } - } - if (found) { - message = *lowest; - } - return found; + return fifo.browse(position, message, unacquired); } bool PriorityQueue::consume(QueuedMessage& message) { if (checkFront()) { - message = messages[frontLevel].front(); + QueuedMessage* pm = messages[frontLevel].front(); messages[frontLevel].pop_front(); clearCache(); + pm->status = QueuedMessage::ACQUIRED; // Updates FIFO index + message = *pm; return true; } else { return false; @@ -120,23 +111,27 @@ bool PriorityQueue::consume(QueuedMessage& message) bool PriorityQueue::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) { - messages[getPriorityLevel(added)].push_back(added); + QueuedMessage* qmp = fifo.pushPtr(added); + messages[getPriorityLevel(added)].push_back(qmp); clearCache(); - return false;//adding a message never causes one to be removed for deque + return false; // Adding a message never causes one to be removed for deque +} + +void PriorityQueue::updateAcquired(const QueuedMessage& acquired) { + fifo.updateAcquired(acquired); } void PriorityQueue::foreach(Functor f) { - for (int i = 0; i < levels; ++i) { - std::for_each(messages[i].begin(), messages[i].end(), f); - } + fifo.foreach(f); } void PriorityQueue::removeIf(Predicate p) { for (int priority = 0; priority < levels; ++priority) { for (Deque::iterator i = messages[priority].begin(); i != messages[priority].end();) { - if (p(*i)) { + if (p(**i)) { + (*i)->status = QueuedMessage::DELETED; // Updates fifo index i = messages[priority].erase(i); clearCache(); } else { @@ -144,6 +139,7 @@ void PriorityQueue::removeIf(Predicate p) } } } + fifo.clean(); } uint PriorityQueue::getPriorityLevel(const QueuedMessage& m) const diff --git a/cpp/src/qpid/broker/PriorityQueue.h b/cpp/src/qpid/broker/PriorityQueue.h index 67c31468d2..8628745db1 100644 --- a/cpp/src/qpid/broker/PriorityQueue.h +++ b/cpp/src/qpid/broker/PriorityQueue.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -21,7 +21,7 @@ * under the License. * */ -#include "qpid/broker/Messages.h" +#include "qpid/broker/MessageDeque.h" #include "qpid/sys/IntegerTypes.h" #include <deque> #include <vector> @@ -32,7 +32,10 @@ namespace broker { /** * Basic priority queue with a configurable number of recognised * priority levels. This is implemented as a separate deque per - * priority level. Browsing is FIFO not priority order. + * priority level. + * + * Browsing is FIFO not priority order. There is a MessageDeque + * for fast browsing. */ class PriorityQueue : public Messages { @@ -48,23 +51,31 @@ class PriorityQueue : public Messages bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); bool consume(QueuedMessage&); bool push(const QueuedMessage& added, QueuedMessage& removed); - + void updateAcquired(const QueuedMessage& acquired); void foreach(Functor); void removeIf(Predicate); + static uint getPriority(const QueuedMessage&); + protected: - typedef std::deque<QueuedMessage> Deque; + typedef std::deque<QueuedMessage*> Deque; typedef std::vector<Deque> PriorityLevels; virtual bool findFrontLevel(uint& p, PriorityLevels&); const int levels; + private: + /** Available messages separated by priority and sorted in priority order. + * Holds pointers to the QueuedMessages in fifo + */ PriorityLevels messages; + /** FIFO index of all messsagse (including acquired messages) for fast browsing and indexing */ + MessageDeque fifo; uint frontLevel; bool haveFront; bool cached; - - bool find(const framing::SequenceNumber&, QueuedMessage&, bool remove); + + void erase(const QueuedMessage&); uint getPriorityLevel(const QueuedMessage&) const; void clearCache(); bool checkFront(); diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp index 015957927f..e7305c021d 100644 --- a/cpp/src/qpid/broker/Queue.cpp +++ b/cpp/src/qpid/broker/Queue.cpp @@ -19,8 +19,9 @@ * */ -#include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" + +#include "qpid/broker/Broker.h" #include "qpid/broker/QueueEvents.h" #include "qpid/broker/Exchange.h" #include "qpid/broker/Fairshare.h" @@ -41,6 +42,7 @@ #include "qpid/management/ManagementAgent.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/sys/ClusterSafe.h" #include "qpid/sys/Monitor.h" #include "qpid/sys/Time.h" @@ -56,7 +58,9 @@ #include <boost/intrusive_ptr.hpp> -using namespace qpid::broker; +namespace qpid { +namespace broker { + using namespace qpid::sys; using namespace qpid::framing; using qpid::management::ManagementAgent; @@ -88,8 +92,57 @@ const std::string qpidInsertSequenceNumbers("qpid.insert_sequence_numbers"); const int ENQUEUE_ONLY=1; const int ENQUEUE_AND_DEQUEUE=2; + +inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg, + _qmf::Queue* mgmtObject, + _qmf::Broker* brokerMgmtObject) +{ + if (mgmtObject != 0) { + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + + uint64_t contentSize = msg->contentSize(); + qStats->msgTotalEnqueues +=1; + bStats->msgTotalEnqueues += 1; + qStats->byteTotalEnqueues += contentSize; + bStats->byteTotalEnqueues += contentSize; + if (msg->isPersistent ()) { + qStats->msgPersistEnqueues += 1; + bStats->msgPersistEnqueues += 1; + qStats->bytePersistEnqueues += contentSize; + bStats->bytePersistEnqueues += contentSize; + } + mgmtObject->statisticsUpdated(); + brokerMgmtObject->statisticsUpdated(); + } +} + +inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg, + _qmf::Queue* mgmtObject, + _qmf::Broker* brokerMgmtObject) +{ + if (mgmtObject != 0){ + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + uint64_t contentSize = msg->contentSize(); + + qStats->msgTotalDequeues += 1; + bStats->msgTotalDequeues += 1; + qStats->byteTotalDequeues += contentSize; + bStats->byteTotalDequeues += contentSize; + if (msg->isPersistent ()){ + qStats->msgPersistDequeues += 1; + bStats->msgPersistDequeues += 1; + qStats->bytePersistDequeues += contentSize; + bStats->bytePersistDequeues += contentSize; + } + mgmtObject->statisticsUpdated(); + brokerMgmtObject->statisticsUpdated(); + } } +} // namespace + Queue::Queue(const string& _name, bool _autodelete, MessageStore* const _store, const OwnershipToken* const _owner, @@ -101,6 +154,7 @@ Queue::Queue(const string& _name, bool _autodelete, store(_store), owner(_owner), consumerCount(0), + browserCount(0), exclusive(0), noLocal(false), persistLastNode(false), @@ -166,7 +220,7 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){ if (msg->isImmediate() && getConsumerCount() == 0) { if (alternateExchange) { DeliverableMessage deliverable(msg); - alternateExchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders()); + alternateExchange->route(deliverable); } } else if (isLocal(msg)) { //drop message @@ -183,11 +237,16 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){ void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg) { + Mutex::ScopedLock locker(messageLock); if (policy.get()) policy->recoverEnqueued(msg); } -void Queue::recover(boost::intrusive_ptr<Message>& msg){ - if (policy.get()) policy->recoverEnqueued(msg); +void Queue::recover(boost::intrusive_ptr<Message>& msg) +{ + { + Mutex::ScopedLock locker(messageLock); + if (policy.get()) policy->recoverEnqueued(msg); + } push(msg, true); if (store){ @@ -209,11 +268,16 @@ void Queue::recover(boost::intrusive_ptr<Message>& msg){ void Queue::process(boost::intrusive_ptr<Message>& msg){ push(msg); if (mgmtObject != 0){ - mgmtObject->inc_msgTxnEnqueues (); - mgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + const uint64_t contentSize = msg->contentSize(); + qStats->msgTxnEnqueues += 1; + qStats->byteTxnEnqueues += contentSize; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgTxnEnqueues (); - brokerMgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgTxnEnqueues += 1; + bStats->byteTxnEnqueues += contentSize; + brokerMgmtObject->statisticsUpdated(); } } } @@ -222,7 +286,6 @@ void Queue::requeue(const QueuedMessage& msg){ assertClusterSafe(); QueueListeners::NotificationSet copy; { - Mutex::ScopedLock locker(messageLock); if (!isEnqueued(msg)) return; if (deleted) { // @@ -238,10 +301,20 @@ void Queue::requeue(const QueuedMessage& msg){ if (brokerMgmtObject) brokerMgmtObject->inc_abandoned(); } - mgntDeqStats(msg.payload); + mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); } else { - messages->release(msg); - listeners.populate(copy); + { + Mutex::ScopedLock locker(messageLock); + messages->release(msg); + observeRequeue(msg, locker); + listeners.populate(copy); + } + + if (mgmtObject) { + mgmtObject->inc_releases(); + if (brokerMgmtObject) + brokerMgmtObject->inc_releases(); + } // for persistLastNode - don't force a message twice to disk, but force it if no force before if(inLastNodeFailure && persistLastNode && !msg.payload->isStoredOnQueue(shared_from_this())) { @@ -251,7 +324,6 @@ void Queue::requeue(const QueuedMessage& msg){ enqueue(0, payload); } } - observeRequeue(msg, locker); } } copy.notify(); @@ -259,10 +331,9 @@ void Queue::requeue(const QueuedMessage& msg){ bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message) { - Mutex::ScopedLock locker(messageLock); assertClusterSafe(); QPID_LOG(debug, "Attempting to acquire message at " << position); - if (acquire(position, message, locker)) { + if (acquire(position, message)) { QPID_LOG(debug, "Acquired message at " << position << " from " << name); return true; } else { @@ -273,17 +344,20 @@ bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& mess bool Queue::acquire(const QueuedMessage& msg, const std::string& consumer) { - Mutex::ScopedLock locker(messageLock); assertClusterSafe(); QPID_LOG(debug, consumer << " attempting to acquire message at " << msg.position); - - if (!allocator->allocate( consumer, msg )) { + bool ok; + { + Mutex::ScopedLock locker(messageLock); + ok = allocator->allocate( consumer, msg ); + } + if (!ok) { QPID_LOG(debug, "Not permitted to acquire msg at " << msg.position << " from '" << name); return false; } QueuedMessage copy(msg); - if (acquire( msg.position, copy, locker)) { + if (acquire( msg.position, copy)) { QPID_LOG(debug, "Acquired message at " << msg.position << " from " << name); return true; } @@ -325,59 +399,73 @@ bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) { while (true) { - Mutex::ScopedLock locker(messageLock); QueuedMessage msg; - if (allocator->nextConsumableMessage(c, msg)) { - if (msg.payload->hasExpired()) { - QPID_LOG(debug, "Message expired from queue '" << name << "'"); - c->setPosition(msg.position); - dequeue(0, msg); - if (mgmtObject) { - mgmtObject->inc_discardsTtl(); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsTtl(); - } + bool found; + { + Mutex::ScopedLock locker(messageLock); + found = allocator->nextConsumableMessage(c, msg); + if (!found) listeners.addListener(c); + } + if (!found) { + QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); + return NO_MESSAGES; + } - continue; + if (msg.payload->hasExpired()) { + QPID_LOG(debug, "Message expired from queue '" << name << "'"); + c->setPosition(msg.position); + dequeue(0, msg); + if (mgmtObject) { + mgmtObject->inc_discardsTtl(); + if (brokerMgmtObject) + brokerMgmtObject->inc_discardsTtl(); } + continue; + } - if (c->filter(msg.payload)) { - if (c->accept(msg.payload)) { + if (c->filter(msg.payload)) { + if (c->accept(msg.payload)) { + { + Mutex::ScopedLock locker(messageLock); bool ok = allocator->allocate( c->getName(), msg ); // inform allocator (void) ok; assert(ok); observeAcquire(msg, locker); - m = msg; - return CONSUMED; - } else { - //message(s) are available but consumer hasn't got enough credit - QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); - messages->release(msg); - return CANT_CONSUME; } + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } + m = msg; + return CONSUMED; } else { - //consumer will never want this message - QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); - messages->release(msg); - return CANT_CONSUME; + //message(s) are available but consumer hasn't got enough credit + QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); } } else { - QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); - listeners.addListener(c); - return NO_MESSAGES; + //consumer will never want this message + QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); } + + Mutex::ScopedLock locker(messageLock); + messages->release(msg); + return CANT_CONSUME; } } bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) { while (true) { - Mutex::ScopedLock locker(messageLock); QueuedMessage msg; - - if (!allocator->nextBrowsableMessage(c, msg)) { // no next available + bool found; + { + Mutex::ScopedLock locker(messageLock); + found = allocator->nextBrowsableMessage(c, msg); + if (!found) listeners.addListener(c); + } + if (!found) { // no next available QPID_LOG(debug, "No browsable messages available for consumer " << c->getName() << " on queue '" << name << "'"); - listeners.addListener(c); return false; } @@ -435,60 +523,67 @@ bool Queue::find(SequenceNumber pos, QueuedMessage& msg) const { void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ assertClusterSafe(); { - Mutex::ScopedLock locker(consumerLock); - if(exclusive) { - throw ResourceLockedException( - QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed.")); - } else if(requestExclusive) { - if(consumerCount) { + Mutex::ScopedLock locker(messageLock); + // NOTE: consumerCount is actually a count of all + // subscriptions, both acquiring and non-acquiring (browsers). + // Check for exclusivity of acquiring consumers. + size_t acquiringConsumers = consumerCount - browserCount; + if (c->preAcquires()) { + if(exclusive) { throw ResourceLockedException( - QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied.")); - } else { - exclusive = c->getSession(); + QPID_MSG("Queue " << getName() + << " has an exclusive consumer. No more consumers allowed.")); + } else if(requestExclusive) { + if(acquiringConsumers) { + throw ResourceLockedException( + QPID_MSG("Queue " << getName() + << " already has consumers. Exclusive access denied.")); + } else { + exclusive = c->getSession(); + } } } + else + browserCount++; consumerCount++; - if (mgmtObject != 0) - mgmtObject->inc_consumerCount (); //reset auto deletion timer if necessary if (autoDeleteTimeout && autoDeleteTask) { autoDeleteTask->cancel(); } + observeConsumerAdd(*c, locker); } - Mutex::ScopedLock locker(messageLock); - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { - try{ - (*i)->consumerAdded(*c); - } catch (const std::exception& e) { - QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what()); - } - } + if (mgmtObject != 0) + mgmtObject->inc_consumerCount (); } void Queue::cancel(Consumer::shared_ptr c){ removeListener(c); { - Mutex::ScopedLock locker(consumerLock); + Mutex::ScopedLock locker(messageLock); consumerCount--; + if (!c->preAcquires()) browserCount--; if(exclusive) exclusive = 0; - if (mgmtObject != 0) - mgmtObject->dec_consumerCount (); - } - Mutex::ScopedLock locker(messageLock); - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { - try{ - (*i)->consumerRemoved(*c); - } catch (const std::exception& e) { - QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what()); - } + observeConsumerRemove(*c, locker); } + if (mgmtObject != 0) + mgmtObject->dec_consumerCount (); } QueuedMessage Queue::get(){ - Mutex::ScopedLock locker(messageLock); QueuedMessage msg(this); - if (messages->consume(msg)) - observeAcquire(msg, locker); + bool ok; + { + Mutex::ScopedLock locker(messageLock); + ok = messages->consume(msg); + if (ok) observeAcquire(msg, locker); + } + + if (ok && mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } + return msg; } @@ -520,22 +615,26 @@ void Queue::purgeExpired(qpid::sys::Duration lapse) messages->removeIf(boost::bind(&collect_if_expired, boost::ref(expired), _1)); } - // - // Report the count of discarded-by-ttl messages - // - if (mgmtObject && !expired.empty()) { - mgmtObject->inc_discardsTtl(expired.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsTtl(expired.size()); - } + if (!expired.empty()) { + if (mgmtObject) { + mgmtObject->inc_acquires(expired.size()); + mgmtObject->inc_discardsTtl(expired.size()); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(expired.size()); + brokerMgmtObject->inc_discardsTtl(expired.size()); + } + } - for (std::deque<QueuedMessage>::const_iterator i = expired.begin(); - i != expired.end(); ++i) { - { - Mutex::ScopedLock locker(messageLock); - observeAcquire(*i, locker); + for (std::deque<QueuedMessage>::const_iterator i = expired.begin(); + i != expired.end(); ++i) { + { + // KAG: should be safe to retake lock after the removeIf, since + // no other thread can touch these messages after the removeIf() call + Mutex::ScopedLock locker(messageLock); + observeAcquire(*i, locker); + } + dequeue( 0, *i ); } - dequeue( 0, *i ); } } } @@ -661,32 +760,46 @@ uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter)); Collector c(*mf.get(), purge_request); - Mutex::ScopedLock locker(messageLock); - messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + { + Mutex::ScopedLock locker(messageLock); + messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + } - if (mgmtObject && !c.matches.empty()) { - if (dest.get()) { - mgmtObject->inc_reroutes(c.matches.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_reroutes(c.matches.size()); - } else { - mgmtObject->inc_discardsPurge(c.matches.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsPurge(c.matches.size()); + if (!c.matches.empty()) { + if (mgmtObject) { + mgmtObject->inc_acquires(c.matches.size()); + if (dest.get()) { + mgmtObject->inc_reroutes(c.matches.size()); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(c.matches.size()); + brokerMgmtObject->inc_reroutes(c.matches.size()); + } + } else { + mgmtObject->inc_discardsPurge(c.matches.size()); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(c.matches.size()); + brokerMgmtObject->inc_discardsPurge(c.matches.size()); + } + } } - } - for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); - qmsg != c.matches.end(); ++qmsg) { - // Update observers and message state: - observeAcquire(*qmsg, locker); - dequeue(0, *qmsg); - QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName()); - // now reroute if necessary - if (dest.get()) { - assert(qmsg->payload); - DeliverableMessage dmsg(qmsg->payload); - dest->routeWithAlternate(dmsg); + for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); + qmsg != c.matches.end(); ++qmsg) { + + { + // KAG: should be safe to retake lock after the removeIf, since + // no other thread can touch these messages after the removeIf call + Mutex::ScopedLock locker(messageLock); + observeAcquire(*qmsg, locker); + } + dequeue(0, *qmsg); + QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName()); + // now reroute if necessary + if (dest.get()) { + assert(qmsg->payload); + DeliverableMessage dmsg(qmsg->payload); + dest->routeWithAlternate(dmsg); + } } } return c.matches.size(); @@ -698,27 +811,51 @@ uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty, std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter)); Collector c(*mf.get(), qty); - Mutex::ScopedLock locker(messageLock); - messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + { + Mutex::ScopedLock locker(messageLock); + messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + } + - for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); - qmsg != c.matches.end(); ++qmsg) { + if (!c.matches.empty()) { // Update observers and message state: - observeAcquire(*qmsg, locker); - dequeue(0, *qmsg); - // and move to destination Queue. - assert(qmsg->payload); - destq->deliver(qmsg->payload); + + if (mgmtObject) { + mgmtObject->inc_acquires(c.matches.size()); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(c.matches.size()); + } + + for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); + qmsg != c.matches.end(); ++qmsg) { + { + Mutex::ScopedLock locker(messageLock); + observeAcquire(*qmsg, locker); + } + dequeue(0, *qmsg); + // and move to destination Queue. + assert(qmsg->payload); + destq->deliver(qmsg->payload); + } } return c.matches.size(); } /** Acquire the message at the given position, return true and msg if acquire succeeds */ -bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg, - const Mutex::ScopedLock& locker) +bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg) { - if (messages->acquire(position, msg)) { - observeAcquire(msg, locker); + bool ok; + { + Mutex::ScopedLock locker(messageLock); + ok = messages->acquire(position, msg); + if (ok) observeAcquire(msg, locker); + } + if (ok) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } ++dequeueSincePurge; return true; } @@ -728,35 +865,43 @@ bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){ assertClusterSafe(); QueueListeners::NotificationSet copy; - QueuedMessage removed; + QueuedMessage removed, qm(this, msg); bool dequeueRequired = false; { Mutex::ScopedLock locker(messageLock); - QueuedMessage qm(this, msg, ++sequence); - if (insertSeqNo) msg->insertCustomProperty(seqNoKey, sequence); - - dequeueRequired = messages->push(qm, removed); - if (dequeueRequired) { + qm.position = ++sequence; + if (messages->push(qm, removed)) { + dequeueRequired = true; observeAcquire(removed, locker); - if (mgmtObject) { - mgmtObject->inc_discardsLvq(); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsLvq(); - } } - listeners.populate(copy); observeEnqueue(qm, locker); + if (policy.get()) { + policy->enqueued(qm); + } + listeners.populate(copy); } - copy.notify(); + if (insertSeqNo) msg->insertCustomProperty(seqNoKey, qm.position); + + mgntEnqStats(msg, mgmtObject, brokerMgmtObject); + if (dequeueRequired) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + mgmtObject->inc_discardsLvq(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + brokerMgmtObject->inc_discardsLvq(); + } if (isRecovery) { //can't issue new requests for the store until //recovery is complete + Mutex::ScopedLock locker(messageLock); pendingDequeues.push_back(removed); } else { dequeue(0, removed); } } + copy.notify(); } void isEnqueueComplete(uint32_t* result, const QueuedMessage& message) @@ -767,8 +912,8 @@ void isEnqueueComplete(uint32_t* result, const QueuedMessage& message) /** function only provided for unit tests, or code not in critical message path */ uint32_t Queue::getEnqueueCompleteMessageCount() const { - Mutex::ScopedLock locker(messageLock); uint32_t count = 0; + Mutex::ScopedLock locker(messageLock); messages->foreach(boost::bind(&isEnqueueComplete, &count, _1)); return count; } @@ -781,13 +926,13 @@ uint32_t Queue::getMessageCount() const uint32_t Queue::getConsumerCount() const { - Mutex::ScopedLock locker(consumerLock); + Mutex::ScopedLock locker(messageLock); return consumerCount; } bool Queue::canAutoDelete() const { - Mutex::ScopedLock locker(consumerLock); + Mutex::ScopedLock locker(messageLock); return autodelete && !consumerCount && !owner; } @@ -894,14 +1039,20 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) { ScopedUse u(barrier); if (!u.acquired) return false; - { Mutex::ScopedLock locker(messageLock); if (!isEnqueued(msg)) return false; if (!ctxt) { + if (policy.get()) policy->dequeued(msg); + messages->deleted(msg); observeDequeue(msg, locker); } } + + if (!ctxt) { + mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); + } + // This check prevents messages which have been forced persistent on one queue from dequeuing // from another on which no forcing has taken place and thus causing a store error. bool fp = msg.payload->isForcedPersistent(); @@ -918,14 +1069,24 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) void Queue::dequeueCommitted(const QueuedMessage& msg) { - Mutex::ScopedLock locker(messageLock); - observeDequeue(msg, locker); + { + Mutex::ScopedLock locker(messageLock); + if (policy.get()) policy->dequeued(msg); + messages->deleted(msg); + observeDequeue(msg, locker); + } + mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); if (mgmtObject != 0) { - mgmtObject->inc_msgTxnDequeues(); - mgmtObject->inc_byteTxnDequeues(msg.payload->contentSize()); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + const uint64_t contentSize = msg.payload->contentSize(); + qStats->msgTxnDequeues += 1; + qStats->byteTxnDequeues += contentSize; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgTxnDequeues(); - brokerMgmtObject->inc_byteTxnDequeues(msg.payload->contentSize()); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgTxnDequeues += 1; + bStats->byteTxnDequeues += contentSize; + brokerMgmtObject->statisticsUpdated(); } } } @@ -934,10 +1095,20 @@ void Queue::dequeueCommitted(const QueuedMessage& msg) * Removes the first (oldest) message from the in-memory delivery queue as well dequeing * it from the logical (and persistent if applicable) queue */ -bool Queue::popAndDequeue(QueuedMessage& msg, const Mutex::ScopedLock& locker) +bool Queue::popAndDequeue(QueuedMessage& msg) { - if (messages->consume(msg)) { - observeAcquire(msg, locker); + bool popped; + { + Mutex::ScopedLock locker(messageLock); + popped = messages->consume(msg); + if (popped) observeAcquire(msg, locker); + } + if (popped) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } dequeue(0, msg); return true; } else { @@ -947,13 +1118,10 @@ bool Queue::popAndDequeue(QueuedMessage& msg, const Mutex::ScopedLock& locker) /** * Updates policy and management when a message has been dequeued, - * expects messageLock to be held + * Requires messageLock be held by caller. */ -void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) +void Queue::observeDequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) { - mgntDeqStats(msg.payload); - if (policy.get()) policy->dequeued(msg); - messages->deleted(msg); for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->dequeued(msg); @@ -963,17 +1131,11 @@ void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) } } -/** updates queue observers when a message has become unavailable for transfer, - * expects messageLock to be held +/** updates queue observers when a message has become unavailable for transfer. + * Requires messageLock be held by caller. */ -void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&) +void Queue::observeAcquire(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) { - if (mgmtObject) { - mgmtObject->inc_acquires(); - if (brokerMgmtObject) - brokerMgmtObject->inc_acquires(); - } - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->acquired(msg); @@ -983,17 +1145,11 @@ void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&) } } -/** updates queue observers when a message has become re-available for transfer, - * expects messageLock to be held +/** updates queue observers when a message has become re-available for transfer + * Requires messageLock be held by caller. */ -void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) +void Queue::observeRequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) { - if (mgmtObject) { - mgmtObject->inc_releases(); - if (brokerMgmtObject) - brokerMgmtObject->inc_releases(); - } - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->requeued(msg); @@ -1003,6 +1159,33 @@ void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) } } +/** updates queue observers when a new consumer has subscribed to this queue. + */ +void Queue::observeConsumerAdd( const Consumer& c, const qpid::sys::Mutex::ScopedLock&) +{ + for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { + try{ + (*i)->consumerAdded(c); + } catch (const std::exception& e) { + QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what()); + } + } +} + +/** updates queue observers when a consumer has unsubscribed from this queue. + */ +void Queue::observeConsumerRemove( const Consumer& c, const qpid::sys::Mutex::ScopedLock&) +{ + for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { + try{ + (*i)->consumerRemoved(c); + } catch (const std::exception& e) { + QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what()); + } + } +} + + void Queue::create(const FieldTable& _settings) { settings = _settings; @@ -1150,23 +1333,21 @@ void Queue::configureImpl(const FieldTable& _settings) void Queue::destroyed() { unbind(broker->getExchanges()); - { - Mutex::ScopedLock locker(messageLock); - QueuedMessage m; - while(popAndDequeue(m, locker)) { - DeliverableMessage msg(m.payload); - if (alternateExchange.get()) { - if (brokerMgmtObject) - brokerMgmtObject->inc_abandonedViaAlt(); - alternateExchange->routeWithAlternate(msg); - } else { - if (brokerMgmtObject) - brokerMgmtObject->inc_abandoned(); - } + + QueuedMessage m; + while(popAndDequeue(m)) { + DeliverableMessage msg(m.payload); + if (alternateExchange.get()) { + if (brokerMgmtObject) + brokerMgmtObject->inc_abandonedViaAlt(); + alternateExchange->routeWithAlternate(msg); + } else { + if (brokerMgmtObject) + brokerMgmtObject->inc_abandoned(); } - if (alternateExchange.get()) - alternateExchange->decAlternateUsers(); } + if (alternateExchange.get()) + alternateExchange->decAlternateUsers(); if (store) { barrier.destroy(); @@ -1177,7 +1358,7 @@ void Queue::destroyed() if (autoDeleteTask) autoDeleteTask = boost::intrusive_ptr<TimerTask>(); notifyDeleted(); { - Mutex::ScopedLock locker(messageLock); + Mutex::ScopedLock lock(messageLock); observers.clear(); } } @@ -1187,8 +1368,8 @@ void Queue::notifyDeleted() QueueListeners::ListenerSet set; { Mutex::ScopedLock locker(messageLock); - listeners.snapshot(set); deleted = true; + listeners.snapshot(set); } set.notifyAll(); } @@ -1206,6 +1387,7 @@ void Queue::unbind(ExchangeRegistry& exchanges) void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) { + Mutex::ScopedLock locker(messageLock); policy = _policy; if (policy.get()) policy->setQueue(this); @@ -1213,6 +1395,7 @@ void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) const QueuePolicy* Queue::getPolicy() { + Mutex::ScopedLock locker(messageLock); return policy.get(); } @@ -1302,7 +1485,7 @@ struct AutoDeleteTask : qpid::sys::TimerTask Queue::shared_ptr queue; AutoDeleteTask(Broker& b, Queue::shared_ptr q, AbsTime fireTime) - : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion"), broker(b), queue(q) {} + : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion:"+q->getName()), broker(b), queue(q) {} void fire() { @@ -1388,11 +1571,15 @@ void Queue::countRejected() const void Queue::countFlowedToDisk(uint64_t size) const { if (mgmtObject) { - mgmtObject->inc_msgFtdEnqueues(); - mgmtObject->inc_byteFtdEnqueues(size); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + qStats->msgFtdEnqueues += 1; + qStats->byteFtdEnqueues += size; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgFtdEnqueues(); - brokerMgmtObject->inc_byteFtdEnqueues(size); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgFtdEnqueues += 1; + bStats->byteFtdEnqueues += size; + brokerMgmtObject->statisticsUpdated(); } } } @@ -1400,11 +1587,15 @@ void Queue::countFlowedToDisk(uint64_t size) const void Queue::countLoadedFromDisk(uint64_t size) const { if (mgmtObject) { - mgmtObject->inc_msgFtdDequeues(); - mgmtObject->inc_byteFtdDequeues(size); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + qStats->msgFtdDequeues += 1; + qStats->byteFtdDequeues += size; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgFtdDequeues(); - brokerMgmtObject->inc_byteFtdDequeues(size); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgFtdDequeues += 1; + bStats->byteFtdDequeues += size; + brokerMgmtObject->statisticsUpdated(); } } } @@ -1434,9 +1625,14 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, str { _qmf::ArgsQueueReroute& rerouteArgs = (_qmf::ArgsQueueReroute&) args; boost::shared_ptr<Exchange> dest; - if (rerouteArgs.i_useAltExchange) + if (rerouteArgs.i_useAltExchange) { + if (!alternateExchange) { + status = Manageable::STATUS_PARAMETER_INVALID; + etext = "No alternate-exchange defined"; + break; + } dest = alternateExchange; - else { + } else { try { dest = broker->getExchanges().get(rerouteArgs.i_exchange); } catch(const std::exception&) { @@ -1486,8 +1682,12 @@ void Queue::recoveryComplete(ExchangeRegistry& exchanges) << "\": exchange does not exist."); } //process any pending dequeues - for_each(pendingDequeues.begin(), pendingDequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); - pendingDequeues.clear(); + std::deque<QueuedMessage> pd; + { + Mutex::ScopedLock locker(messageLock); + pendingDequeues.swap(pd); + } + for_each(pd.begin(), pd.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); } void Queue::insertSequenceNumbers(const std::string& key) @@ -1497,10 +1697,10 @@ void Queue::insertSequenceNumbers(const std::string& key) QPID_LOG(debug, "Inserting sequence numbers as " << key); } -/** updates queue observers and state when a message has become available for transfer, - * expects messageLock to be held +/** updates queue observers and state when a message has become available for transfer + * Requires messageLock be held by caller. */ -void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&) +void Queue::observeEnqueue(const QueuedMessage& m, const qpid::sys::Mutex::ScopedLock&) { for (Observers::iterator i = observers.begin(); i != observers.end(); ++i) { try { @@ -1509,10 +1709,6 @@ void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&) QPID_LOG(warning, "Exception on notification of enqueue for queue " << getName() << ": " << e.what()); } } - if (policy.get()) { - policy->enqueued(m); - } - mgntEnqStats(m.payload); } void Queue::updateEnqueued(const QueuedMessage& m) @@ -1520,12 +1716,16 @@ void Queue::updateEnqueued(const QueuedMessage& m) if (m.payload) { boost::intrusive_ptr<Message> payload = m.payload; enqueue(0, payload, true); - messages->updateAcquired(m); - if (policy.get()) { - policy->recoverEnqueued(payload); + { + Mutex::ScopedLock locker(messageLock); + messages->updateAcquired(m); + observeEnqueue(m, locker); + if (policy.get()) { + policy->recoverEnqueued(payload); + policy->enqueued(m); + } } - Mutex::ScopedLock locker(messageLock); - observeEnqueue(m, locker); + mgntEnqStats(m.payload, mgmtObject, brokerMgmtObject); } else { QPID_LOG(warning, "Queue informed of enqueued message that has no payload"); } @@ -1533,10 +1733,16 @@ void Queue::updateEnqueued(const QueuedMessage& m) bool Queue::isEnqueued(const QueuedMessage& msg) { + Mutex::ScopedLock locker(messageLock); return !policy.get() || policy->isEnqueued(msg); } +// Note: accessing listeners outside of lock is dangerous. Caller must ensure the queue's +// state is not changed while listeners is referenced. QueueListeners& Queue::getListeners() { return listeners; } + +// Note: accessing messages outside of lock is dangerous. Caller must ensure the queue's +// state is not changed while messages is referenced. Messages& Queue::getMessages() { return *messages; } const Messages& Queue::getMessages() const { return *messages; } @@ -1549,13 +1755,13 @@ void Queue::checkNotDeleted(const Consumer::shared_ptr& c) void Queue::addObserver(boost::shared_ptr<QueueObserver> observer) { - Mutex::ScopedLock locker(messageLock); + Mutex::ScopedLock lock(messageLock); observers.insert(observer); } void Queue::removeObserver(boost::shared_ptr<QueueObserver> observer) { - Mutex::ScopedLock locker(messageLock); + Mutex::ScopedLock lock(messageLock); observers.erase(observer); } @@ -1618,7 +1824,7 @@ Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {} bool Queue::UsageBarrier::acquire() { - Monitor::ScopedLock l(parent.messageLock); + Monitor::ScopedLock l(parent.messageLock); /** @todo: use a dedicated lock instead of messageLock */ if (parent.deleted) { return false; } else { @@ -1639,3 +1845,6 @@ void Queue::UsageBarrier::destroy() parent.deleted = true; while (count) parent.messageLock.wait(); } + +}} + diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h index e8573c17cc..9869a698c1 100644 --- a/cpp/src/qpid/broker/Queue.h +++ b/cpp/src/qpid/broker/Queue.h @@ -97,7 +97,8 @@ class Queue : public boost::enable_shared_from_this<Queue>, const bool autodelete; MessageStore* store; const OwnershipToken* owner; - uint32_t consumerCount; + uint32_t consumerCount; // Actually a count of all subscriptions, acquiring or not. + uint32_t browserCount; // Count of non-acquiring subscriptions. OwnershipToken* exclusive; bool noLocal; bool persistLastNode; @@ -107,7 +108,22 @@ class Queue : public boost::enable_shared_from_this<Queue>, QueueListeners listeners; std::auto_ptr<Messages> messages; std::deque<QueuedMessage> pendingDequeues;//used to avoid dequeuing during recovery - mutable qpid::sys::Mutex consumerLock; + /** messageLock is used to keep the Queue's state consistent while processing message + * events, such as message dispatch, enqueue, acquire, and dequeue. It must be held + * while updating certain members in order to keep these members consistent with + * each other: + * o messages + * o sequence + * o policy + * o listeners + * o allocator + * o observeXXX() methods + * o observers + * o pendingDequeues (TBD: move under separate lock) + * o exclusive OwnershipToken (TBD: move under separate lock) + * o consumerCount (TBD: move under separate lock) + * o Queue::UsageBarrier (TBD: move under separate lock) + */ mutable qpid::sys::Monitor messageLock; mutable qpid::sys::Mutex ownershipLock; mutable uint64_t persistenceId; @@ -143,52 +159,20 @@ class Queue : public boost::enable_shared_from_this<Queue>, bool isExcluded(boost::intrusive_ptr<Message>& msg); - /** update queue observers, stats, policy, etc when the messages' state changes. Lock - * must be held by caller */ + /** update queue observers, stats, policy, etc when the messages' state changes. + * messageLock is held by caller */ void observeEnqueue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); void observeAcquire(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); void observeRequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); void observeDequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); - bool popAndDequeue(QueuedMessage&, const sys::Mutex::ScopedLock& lock); - // acquire message @ position, return true and set msg if acquire succeeds - bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg, - const sys::Mutex::ScopedLock& held); + void observeConsumerAdd( const Consumer&, const sys::Mutex::ScopedLock& lock); + void observeConsumerRemove( const Consumer&, const sys::Mutex::ScopedLock& lock); + bool popAndDequeue(QueuedMessage&); + bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg); void forcePersistent(QueuedMessage& msg); int getEventMode(); void configureImpl(const qpid::framing::FieldTable& settings); - - inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg) - { - if (mgmtObject != 0) { - mgmtObject->inc_msgTotalEnqueues (); - mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - brokerMgmtObject->inc_msgTotalEnqueues (); - brokerMgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - if (msg->isPersistent ()) { - mgmtObject->inc_msgPersistEnqueues (); - mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); - brokerMgmtObject->inc_msgPersistEnqueues (); - brokerMgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); - } - } - } - inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg) - { - if (mgmtObject != 0){ - mgmtObject->inc_msgTotalDequeues (); - mgmtObject->inc_byteTotalDequeues (msg->contentSize()); - brokerMgmtObject->inc_msgTotalDequeues (); - brokerMgmtObject->inc_byteTotalDequeues (msg->contentSize()); - if (msg->isPersistent ()){ - mgmtObject->inc_msgPersistDequeues (); - mgmtObject->inc_bytePersistDequeues (msg->contentSize()); - brokerMgmtObject->inc_msgPersistDequeues (); - brokerMgmtObject->inc_bytePersistDequeues (msg->contentSize()); - } - } - } - void checkNotDeleted(const Consumer::shared_ptr& c); void notifyDeleted(); @@ -235,8 +219,9 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** * Bind self to specified exchange, and record that binding for unbinding on delete. */ - bool bind(boost::shared_ptr<Exchange> exchange, const std::string& key, - const qpid::framing::FieldTable& arguments=qpid::framing::FieldTable()); + QPID_BROKER_EXTERN bool bind( + boost::shared_ptr<Exchange> exchange, const std::string& key, + const qpid::framing::FieldTable& arguments=qpid::framing::FieldTable()); /** Acquire the message at the given position if it is available for acquire. Not to * be used by clients, but used by the broker for queue management. @@ -271,28 +256,29 @@ class Queue : public boost::enable_shared_from_this<Queue>, bool exclusive = false); QPID_BROKER_EXTERN void cancel(Consumer::shared_ptr c); - uint32_t purge(const uint32_t purge_request=0, //defaults to all messages + QPID_BROKER_EXTERN uint32_t purge(const uint32_t purge_request=0, //defaults to all messages boost::shared_ptr<Exchange> dest=boost::shared_ptr<Exchange>(), const ::qpid::types::Variant::Map *filter=0); QPID_BROKER_EXTERN void purgeExpired(sys::Duration); //move qty # of messages to destination Queue destq - uint32_t move(const Queue::shared_ptr destq, uint32_t qty, - const qpid::types::Variant::Map *filter=0); + QPID_BROKER_EXTERN uint32_t move( + const Queue::shared_ptr destq, uint32_t qty, + const qpid::types::Variant::Map *filter=0); QPID_BROKER_EXTERN uint32_t getMessageCount() const; QPID_BROKER_EXTERN uint32_t getEnqueueCompleteMessageCount() const; QPID_BROKER_EXTERN uint32_t getConsumerCount() const; inline const std::string& getName() const { return name; } - bool isExclusiveOwner(const OwnershipToken* const o) const; - void releaseExclusiveOwnership(); - bool setExclusiveOwner(const OwnershipToken* const o); - bool hasExclusiveConsumer() const; - bool hasExclusiveOwner() const; + QPID_BROKER_EXTERN bool isExclusiveOwner(const OwnershipToken* const o) const; + QPID_BROKER_EXTERN void releaseExclusiveOwnership(); + QPID_BROKER_EXTERN bool setExclusiveOwner(const OwnershipToken* const o); + QPID_BROKER_EXTERN bool hasExclusiveConsumer() const; + QPID_BROKER_EXTERN bool hasExclusiveOwner() const; inline bool isDurable() const { return store != 0; } inline const framing::FieldTable& getSettings() const { return settings; } inline bool isAutoDelete() const { return autodelete; } - bool canAutoDelete() const; + QPID_BROKER_EXTERN bool canAutoDelete() const; const QueueBindings& getBindings() const { return bindings; } /** @@ -301,8 +287,8 @@ class Queue : public boost::enable_shared_from_this<Queue>, QPID_BROKER_EXTERN void setLastNodeFailure(); QPID_BROKER_EXTERN void clearLastNodeFailure(); - bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck = false); - void enqueueAborted(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck = false); + QPID_BROKER_EXTERN void enqueueAborted(boost::intrusive_ptr<Message> msg); /** * dequeue from store (only done once messages is acknowledged) */ @@ -311,7 +297,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * Inform the queue that a previous transactional dequeue * committed. */ - void dequeueCommitted(const QueuedMessage& msg); + QPID_BROKER_EXTERN void dequeueCommitted(const QueuedMessage& msg); /** * Inform queue of messages that were enqueued, have since @@ -319,7 +305,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * thus are still logically on the queue) - used in * clustered broker. */ - void updateEnqueued(const QueuedMessage& msg); + QPID_BROKER_EXTERN void updateEnqueued(const QueuedMessage& msg); /** * Test whether the specified message (identified by its @@ -328,7 +314,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * have been delievered to a subscriber who has not yet * accepted it). */ - bool isEnqueued(const QueuedMessage& msg); + QPID_BROKER_EXTERN bool isEnqueued(const QueuedMessage& msg); /** * Acquires the next available (oldest) message @@ -338,17 +324,17 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** Get the message at position pos, returns true if found and sets msg */ QPID_BROKER_EXTERN bool find(framing::SequenceNumber pos, QueuedMessage& msg ) const; - const QueuePolicy* getPolicy(); + QPID_BROKER_EXTERN const QueuePolicy* getPolicy(); - void setAlternateExchange(boost::shared_ptr<Exchange> exchange); - boost::shared_ptr<Exchange> getAlternateExchange(); - bool isLocal(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void setAlternateExchange(boost::shared_ptr<Exchange> exchange); + QPID_BROKER_EXTERN boost::shared_ptr<Exchange> getAlternateExchange(); + QPID_BROKER_EXTERN bool isLocal(boost::intrusive_ptr<Message>& msg); //PersistableQueue support: - uint64_t getPersistenceId() const; - void setPersistenceId(uint64_t persistenceId) const; - void encode(framing::Buffer& buffer) const; - uint32_t encodedSize() const; + QPID_BROKER_EXTERN uint64_t getPersistenceId() const; + QPID_BROKER_EXTERN void setPersistenceId(uint64_t persistenceId) const; + QPID_BROKER_EXTERN void encode(framing::Buffer& buffer) const; + QPID_BROKER_EXTERN uint32_t encodedSize() const; /** * Restores a queue from encoded data (used in recovery) @@ -362,15 +348,15 @@ class Queue : public boost::enable_shared_from_this<Queue>, virtual void setExternalQueueStore(ExternalQueueStore* inst); // Increment the rejected-by-consumer counter. - void countRejected() const; - void countFlowedToDisk(uint64_t size) const; - void countLoadedFromDisk(uint64_t size) const; + QPID_BROKER_EXTERN void countRejected() const; + QPID_BROKER_EXTERN void countFlowedToDisk(uint64_t size) const; + QPID_BROKER_EXTERN void countLoadedFromDisk(uint64_t size) const; // Manageable entry points - management::ManagementObject* GetManagementObject (void) const; + QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject (void) const; management::Manageable::status_t - ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); - void query(::qpid::types::Variant::Map&) const; + QPID_BROKER_EXTERN ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + QPID_BROKER_EXTERN void query(::qpid::types::Variant::Map&) const; /** Apply f to each Message on the queue. */ template <class F> void eachMessage(F f) { @@ -385,6 +371,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** Apply f to each Observer on the queue */ template <class F> void eachObserver(F f) { + sys::Mutex::ScopedLock l(messageLock); std::for_each<Observers::iterator, F>(observers.begin(), observers.end(), f); } @@ -396,31 +383,31 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** return current position sequence number for the next message on the queue. */ QPID_BROKER_EXTERN framing::SequenceNumber getPosition(); - void addObserver(boost::shared_ptr<QueueObserver>); - void removeObserver(boost::shared_ptr<QueueObserver>); + QPID_BROKER_EXTERN void addObserver(boost::shared_ptr<QueueObserver>); + QPID_BROKER_EXTERN void removeObserver(boost::shared_ptr<QueueObserver>); QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key); /** * Notify queue that recovery has completed. */ - void recoveryComplete(ExchangeRegistry& exchanges); + QPID_BROKER_EXTERN void recoveryComplete(ExchangeRegistry& exchanges); // For cluster update - QueueListeners& getListeners(); - Messages& getMessages(); - const Messages& getMessages() const; + QPID_BROKER_EXTERN QueueListeners& getListeners(); + QPID_BROKER_EXTERN Messages& getMessages(); + QPID_BROKER_EXTERN const Messages& getMessages() const; /** * Reserve space in policy for an enqueued message that * has been recovered in the prepared state (dtx only) */ - void recoverPrepared(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void recoverPrepared(boost::intrusive_ptr<Message>& msg); - void flush(); + QPID_BROKER_EXTERN void flush(); - Broker* getBroker(); + QPID_BROKER_EXTERN Broker* getBroker(); uint32_t getDequeueSincePurge() { return dequeueSincePurge.get(); } - void setDequeueSincePurge(uint32_t value); + QPID_BROKER_EXTERN void setDequeueSincePurge(uint32_t value); }; } } diff --git a/cpp/src/qpid/broker/QueueListeners.cpp b/cpp/src/qpid/broker/QueueListeners.cpp index 32c208b073..0338a674cf 100644 --- a/cpp/src/qpid/broker/QueueListeners.cpp +++ b/cpp/src/qpid/broker/QueueListeners.cpp @@ -79,10 +79,6 @@ void QueueListeners::NotificationSet::notify() std::for_each(browsers.begin(), browsers.end(), boost::mem_fn(&Consumer::notify)); } -bool QueueListeners::contains(Consumer::shared_ptr c) const { - return c->inListeners; -} - void QueueListeners::ListenerSet::notifyAll() { std::for_each(listeners.begin(), listeners.end(), boost::mem_fn(&Consumer::notify)); diff --git a/cpp/src/qpid/broker/QueueListeners.h b/cpp/src/qpid/broker/QueueListeners.h index 0659499253..ca844fd47e 100644 --- a/cpp/src/qpid/broker/QueueListeners.h +++ b/cpp/src/qpid/broker/QueueListeners.h @@ -30,7 +30,7 @@ namespace broker { /** * Track and notify components that wish to be notified of messages * that become available on a queue. - * + * * None of the methods defined here are protected by locking. However * the populate method allows a 'snapshot' to be taken of the * listeners to be notified. NotificationSet::notify() may then be @@ -61,11 +61,10 @@ class QueueListeners friend class QueueListeners; }; - void addListener(Consumer::shared_ptr); - void removeListener(Consumer::shared_ptr); + void addListener(Consumer::shared_ptr); + void removeListener(Consumer::shared_ptr); void populate(NotificationSet&); void snapshot(ListenerSet&); - bool contains(Consumer::shared_ptr c) const; void notifyAll(); template <class F> void eachListener(F f) { diff --git a/cpp/src/qpid/sys/apr/Time.cpp b/cpp/src/qpid/broker/QueuedMessage.cpp index 34e740b144..d40cc901ff 100644 --- a/cpp/src/qpid/sys/apr/Time.cpp +++ b/cpp/src/qpid/broker/QueuedMessage.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,19 +18,17 @@ * under the License. * */ - -#include "qpid/sys/Time.h" - -#include <apr_time.h> +#include "QueuedMessage.h" +#include "Queue.h" +#include <iostream> namespace qpid { -namespace sys { - -AbsTime AbsTime::now() { - AbsTime time_now; - time_now.time_ns = apr_time_now() * TIME_USEC; - return time_now; +namespace broker { + +std::ostream& operator<<(std::ostream& o, const QueuedMessage& qm) { + o << (qm.queue ? qm.queue->getName() : std::string()) << "[" << qm.position <<"]"; + return o; } -}} +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueuedMessage.h b/cpp/src/qpid/broker/QueuedMessage.h index 806da8e720..9d008193a0 100644 --- a/cpp/src/qpid/broker/QueuedMessage.h +++ b/cpp/src/qpid/broker/QueuedMessage.h @@ -22,6 +22,8 @@ #define _QueuedMessage_ #include "qpid/broker/Message.h" +#include "BrokerImportExport.h" +#include <iosfwd> namespace qpid { namespace broker { @@ -47,6 +49,7 @@ inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) { return a.position < b.position; } +QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueuedMessage&); }} diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp index d7adbd68ab..80fa5e1c0e 100644 --- a/cpp/src/qpid/broker/SaslAuthenticator.cpp +++ b/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/framing/FieldValue.h" #include "qpid/sys/SecuritySettings.h" #include <boost/format.hpp> diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp index e7d2259c80..64924bdd4c 100644 --- a/cpp/src/qpid/broker/SemanticState.cpp +++ b/cpp/src/qpid/broker/SemanticState.cpp @@ -489,14 +489,14 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { exchangeName << " with routing-key " << msg->getRoutingKey())); } - cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + cacheExchange->route(strategy); if (!strategy.delivered) { //TODO:if discard-unroutable, just drop it //TODO:else if accept-mode is explicit, reject it //else route it to alternate exchange if (cacheExchange->getAlternate()) { - cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + cacheExchange->getAlternate()->route(strategy); } if (!strategy.delivered) { msg->destroy(); diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h index 5a83fd0fb3..e5e1d2da16 100644 --- a/cpp/src/qpid/broker/SemanticState.h +++ b/cpp/src/qpid/broker/SemanticState.h @@ -22,6 +22,7 @@ * */ +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/Consumer.h" #include "qpid/broker/Credit.h" #include "qpid/broker/Deliverable.h" @@ -39,7 +40,6 @@ #include "qpid/sys/AggregateOutput.h" #include "qpid/sys/Mutex.h" #include "qpid/sys/AtomicValue.h" -#include "qpid/broker/AclModule.h" #include "qmf/org/apache/qpid/broker/Subscription.h" #include <list> @@ -99,42 +99,44 @@ class SemanticState : private boost::noncopyable { bool haveCredit(); protected: - virtual bool doDispatch(); + QPID_BROKER_EXTERN virtual bool doDispatch(); size_t unacked() { return parent->unacked.size(); } public: typedef boost::shared_ptr<ConsumerImpl> shared_ptr; - ConsumerImpl(SemanticState* parent, - const std::string& name, boost::shared_ptr<Queue> queue, - bool ack, bool acquire, bool exclusive, - const std::string& tag, const std::string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments); - virtual ~ConsumerImpl(); - OwnershipToken* getSession(); - virtual bool deliver(QueuedMessage& msg); - bool filter(boost::intrusive_ptr<Message> msg); - bool accept(boost::intrusive_ptr<Message> msg); - void cancel() {} - - void disableNotify(); - void enableNotify(); - void notify(); - bool isNotifyEnabled() const; - - void requestDispatch(); - - void setWindowMode(); - void setCreditMode(); - void addByteCredit(uint32_t value); - void addMessageCredit(uint32_t value); - void flush(); - void stop(); - void complete(DeliveryRecord&); + QPID_BROKER_EXTERN ConsumerImpl( + SemanticState* parent, + const std::string& name, boost::shared_ptr<Queue> queue, + bool ack, bool acquire, bool exclusive, + const std::string& tag, const std::string& resumeId, uint64_t resumeTtl, + const framing::FieldTable& arguments); + QPID_BROKER_EXTERN virtual ~ConsumerImpl(); + QPID_BROKER_EXTERN OwnershipToken* getSession(); + QPID_BROKER_EXTERN virtual bool deliver(QueuedMessage& msg); + QPID_BROKER_EXTERN bool filter(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN bool accept(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN void cancel() {} + + QPID_BROKER_EXTERN void disableNotify(); + QPID_BROKER_EXTERN void enableNotify(); + QPID_BROKER_EXTERN void notify(); + QPID_BROKER_EXTERN bool isNotifyEnabled() const; + + QPID_BROKER_EXTERN void requestDispatch(); + + QPID_BROKER_EXTERN void setWindowMode(); + QPID_BROKER_EXTERN void setCreditMode(); + QPID_BROKER_EXTERN void addByteCredit(uint32_t value); + QPID_BROKER_EXTERN void addMessageCredit(uint32_t value); + QPID_BROKER_EXTERN void flush(); + QPID_BROKER_EXTERN void stop(); + QPID_BROKER_EXTERN void complete(DeliveryRecord&); boost::shared_ptr<Queue> getQueue() const { return queue; } bool isBlocked() const { return blocked; } bool setBlocked(bool set) { std::swap(set, blocked); return set; } - bool doOutput(); + QPID_BROKER_EXTERN bool doOutput(); Credit& getCredit() { return credit; } const Credit& getCredit() const { return credit; } @@ -152,8 +154,11 @@ class SemanticState : private boost::noncopyable { void acknowledged(const broker::QueuedMessage&) {} // manageable entry points - management::ManagementObject* GetManagementObject (void) const; - management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + QPID_BROKER_EXTERN management::ManagementObject* + GetManagementObject(void) const; + + QPID_BROKER_EXTERN management::Manageable::status_t + ManagementMethod(uint32_t methodId, management::Args& args, std::string& text); }; typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap; diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp index 4aad46f782..78f2e43ce0 100644 --- a/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/cpp/src/qpid/broker/SessionAdapter.cpp @@ -21,8 +21,9 @@ #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/enum.h" -#include "qpid/log/Statement.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/SequenceSet.h" +#include "qpid/log/Statement.h" #include "qpid/management/ManagementAgent.h" #include "qpid/broker/SessionState.h" #include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h" @@ -73,18 +74,12 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const if(passive){ AclModule* acl = getBroker().getAcl(); if (acl) { - //TODO: why does a passive declare require create - //permission? The purpose of the passive flag is to state - //that the exchange should *not* created. For - //authorisation a passive declare is similar to - //exchange-query. std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_TYPE, type)); params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _TRUE)); params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); - if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,¶ms) ) - throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << getConnection().getUserId())); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchange,¶ms) ) + throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange access request from " << getConnection().getUserId())); } Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange)); checkType(actual, type); @@ -274,22 +269,16 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& if (passive && !name.empty()) { AclModule* acl = getBroker().getAcl(); if (acl) { - //TODO: why does a passive declare require create - //permission? The purpose of the passive flag is to state - //that the queue should *not* created. For - //authorisation a passive declare is similar to - //queue-query (or indeed a qmf query). std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _TRUE)); params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type"))); params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count")))); params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size")))); - if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) - throw UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId())); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,¶ms) ) + throw UnauthorizedAccessException(QPID_MSG("ACL denied queue access request from " << getConnection().getUserId())); } queue = getQueue(name); //TODO: check alternate-exchange is as expected @@ -409,6 +398,7 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, if(!destination.empty() && state.exists(destination)) throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + // We allow browsing (acquireMode == 1) of exclusive queues, this is required by HA. if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session) && acquireMode == 0) throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue " << queue->getName())); @@ -548,13 +538,6 @@ void SessionAdapter::TxHandlerImpl::rollback() state.rollback(); } -std::string SessionAdapter::DtxHandlerImpl::convert(const framing::Xid& xid) -{ - std::string encoded; - encode(xid, encoded); - return encoded; -} - void SessionAdapter::DtxHandlerImpl::select() { state.selectDtx(); @@ -566,7 +549,7 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, { try { if (fail) { - state.endDtx(convert(xid), true); + state.endDtx(DtxManager::convert(xid), true); if (suspend) { throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set.")); } else { @@ -574,9 +557,9 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, } } else { if (suspend) { - state.suspendDtx(convert(xid)); + state.suspendDtx(DtxManager::convert(xid)); } else { - state.endDtx(convert(xid), false); + state.endDtx(DtxManager::convert(xid), false); } return XaResult(XA_STATUS_XA_OK); } @@ -594,9 +577,9 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid, } try { if (resume) { - state.resumeDtx(convert(xid)); + state.resumeDtx(DtxManager::convert(xid)); } else { - state.startDtx(convert(xid), getBroker().getDtxManager(), join); + state.startDtx(DtxManager::convert(xid), getBroker().getDtxManager(), join); } return XaResult(XA_STATUS_XA_OK); } catch (const DtxTimeoutException& /*e*/) { @@ -607,7 +590,7 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid, XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid) { try { - bool ok = getBroker().getDtxManager().prepare(convert(xid)); + bool ok = getBroker().getDtxManager().prepare(DtxManager::convert(xid)); return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); } catch (const DtxTimeoutException& /*e*/) { return XaResult(XA_STATUS_XA_RBTIMEOUT); @@ -618,7 +601,7 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid, bool onePhase) { try { - bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase); + bool ok = getBroker().getDtxManager().commit(DtxManager::convert(xid), onePhase); return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); } catch (const DtxTimeoutException& /*e*/) { return XaResult(XA_STATUS_XA_RBTIMEOUT); @@ -629,7 +612,7 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid, XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid) { try { - getBroker().getDtxManager().rollback(convert(xid)); + getBroker().getDtxManager().rollback(DtxManager::convert(xid)); return XaResult(XA_STATUS_XA_OK); } catch (const DtxTimeoutException& /*e*/) { return XaResult(XA_STATUS_XA_RBTIMEOUT); @@ -659,7 +642,7 @@ void SessionAdapter::DtxHandlerImpl::forget(const Xid& xid) DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid) { - uint32_t timeout = getBroker().getDtxManager().getTimeout(convert(xid)); + uint32_t timeout = getBroker().getDtxManager().getTimeout(DtxManager::convert(xid)); return DtxGetTimeoutResult(timeout); } @@ -667,7 +650,7 @@ DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid) void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid, uint32_t timeout) { - getBroker().getDtxManager().setTimeout(convert(xid), timeout); + getBroker().getDtxManager().setTimeout(DtxManager::convert(xid), timeout); } diff --git a/cpp/src/qpid/broker/SessionAdapter.h b/cpp/src/qpid/broker/SessionAdapter.h index 8987c4812f..bc056538b1 100644 --- a/cpp/src/qpid/broker/SessionAdapter.h +++ b/cpp/src/qpid/broker/SessionAdapter.h @@ -226,10 +226,8 @@ class Queue; void rollback(); }; - class DtxHandlerImpl : public DtxHandler, public HandlerHelper, private framing::StructHelper + class DtxHandlerImpl : public DtxHandler, public HandlerHelper { - std::string convert(const framing::Xid& xid); - public: DtxHandlerImpl(SemanticState& session) : HandlerHelper(session) {} diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp index 752fa55535..b58c7c01c5 100644 --- a/cpp/src/qpid/broker/SessionHandler.cpp +++ b/cpp/src/qpid/broker/SessionHandler.cpp @@ -64,6 +64,7 @@ void SessionHandler::handleDetach() { if (session.get()) connection.getBroker().getSessionManager().detach(session); assert(!session.get()); + if (detachedCallback) detachedCallback(); connection.closeChannel(channel.get()); } @@ -117,4 +118,8 @@ void SessionHandler::attached(const std::string& name) } } +void SessionHandler::setDetachedCallback(boost::function<void()> cb) { + detachedCallback = cb; +} + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h index 8cd5072574..4e2cfaa963 100644 --- a/cpp/src/qpid/broker/SessionHandler.h +++ b/cpp/src/qpid/broker/SessionHandler.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,7 @@ #include "qpid/amqp_0_10/SessionHandler.h" #include "qpid/broker/SessionHandler.h" #include "qpid/framing/AMQP_ClientProxy.h" +#include <boost/function.hpp> namespace qpid { class SessionState; @@ -61,7 +62,7 @@ class SessionHandler : public amqp_0_10::SessionHandler { * This proxy is for sending such commands. In a clustered broker it will take steps * to synchronize command order across the cluster. In a stand-alone broker * it is just a synonym for getProxy() - */ + */ framing::AMQP_ClientProxy& getClusterOrderProxy() { return clusterOrderProxy.get() ? *clusterOrderProxy : proxy; } @@ -70,6 +71,8 @@ class SessionHandler : public amqp_0_10::SessionHandler { void attached(const std::string& name);//used by 'pushing' inter-broker bridges void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges + void setDetachedCallback(boost::function<void()> cb); + protected: virtual void setState(const std::string& sessionName, bool force); virtual qpid::SessionState* getState(); @@ -91,6 +94,7 @@ class SessionHandler : public amqp_0_10::SessionHandler { framing::AMQP_ClientProxy proxy; std::auto_ptr<SessionState> session; std::auto_ptr<SetChannelProxy> clusterOrderProxy; + boost::function<void ()> detachedCallback; }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp index 644a3d628e..dd3ec13019 100644 --- a/cpp/src/qpid/broker/TopicExchange.cpp +++ b/cpp/src/qpid/broker/TopicExchange.cpp @@ -350,8 +350,9 @@ TopicExchange::BindingKey *TopicExchange::getQueueBinding(Queue::shared_ptr queu return (q != qv.end()) ? bk : 0; } -void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +void TopicExchange::route(Deliverable& msg) { + const string& routingKey = msg.getMessage().getRoutingKey(); // Note: PERFORMANCE CRITICAL!!! BindingList b; std::map<std::string, BindingList>::iterator it; diff --git a/cpp/src/qpid/broker/TopicExchange.h b/cpp/src/qpid/broker/TopicExchange.h index 636918f8a1..cc24e1411e 100644 --- a/cpp/src/qpid/broker/TopicExchange.h +++ b/cpp/src/qpid/broker/TopicExchange.h @@ -185,9 +185,7 @@ class TopicExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp index 2acc09cded..a38e6ac12a 100644 --- a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp +++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp @@ -25,6 +25,7 @@ #include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include <windows.h> diff --git a/cpp/src/qpid/client/Connection.cpp b/cpp/src/qpid/client/Connection.cpp index 2882ef5d42..83a4a35b53 100644 --- a/cpp/src/qpid/client/Connection.cpp +++ b/cpp/src/qpid/client/Connection.cpp @@ -75,7 +75,7 @@ void Connection::open(const Url& url, const ConnectionSettings& settings) { i++; try { ConnectionSettings cs(settings); - cs.protocol = addr.protocol; + if (addr.protocol.size()) cs.protocol = addr.protocol; cs.host = addr.host; cs.port = addr.port; open(cs); diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp index ab0d8e0700..94561f8079 100644 --- a/cpp/src/qpid/client/ConnectionHandler.cpp +++ b/cpp/src/qpid/client/ConnectionHandler.cpp @@ -28,10 +28,13 @@ #include "qpid/framing/all_method_bodies.h" #include "qpid/framing/ClientInvoker.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Helpers.h" #include "qpid/log/Statement.h" #include "qpid/sys/SystemInfo.h" +#include <algorithm> + using namespace qpid::client; using namespace qpid::framing; using namespace qpid::framing::connection; @@ -238,15 +241,16 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me ); std::vector<std::string> mechlist; + mechlist.reserve(mechanisms.size()); if (mechanism.empty()) { //mechlist is simply what the server offers - mechanisms.collect(mechlist); + std::transform(mechanisms.begin(), mechanisms.end(), std::back_inserter(mechlist), Array::get<std::string, Array::ValuePtr>); } else { //mechlist is the intersection of those indicated by user and //those supported by server, in the order listed by user std::vector<std::string> allowed = split(mechanism, " "); - std::vector<std::string> supported; - mechanisms.collect(supported); + std::vector<std::string> supported(mechanisms.size()); + std::transform(mechanisms.begin(), mechanisms.end(), std::back_inserter(supported), Array::get<std::string, Array::ValuePtr>); intersection(allowed, supported, mechlist); if (mechlist.empty()) { throw Exception(QPID_MSG("Desired mechanism(s) not valid: " << mechanism << " (supported: " << join(supported) << ")")); diff --git a/cpp/src/qpid/client/ConnectionImpl.cpp b/cpp/src/qpid/client/ConnectionImpl.cpp index db97f1e0f4..85b0e8303e 100644 --- a/cpp/src/qpid/client/ConnectionImpl.cpp +++ b/cpp/src/qpid/client/ConnectionImpl.cpp @@ -115,8 +115,10 @@ public: ioThreads(0), connections(0) { + CommonOptions common("", "", QPIDC_CONF_FILE); IOThreadOptions options(c); - options.parse(0, 0, QPIDC_CONF_FILE, true); + common.parse(0, 0, common.clientConfig, true); + options.parse(0, 0, common.clientConfig, true); maxIOThreads = (options.maxIOThreads != -1) ? options.maxIOThreads : 1; } diff --git a/cpp/src/qpid/client/LoadPlugins.cpp b/cpp/src/qpid/client/LoadPlugins.cpp index 246eb60c67..d76e1d458e 100644 --- a/cpp/src/qpid/client/LoadPlugins.cpp +++ b/cpp/src/qpid/client/LoadPlugins.cpp @@ -39,10 +39,12 @@ namespace { struct LoadtimeInitialise { LoadtimeInitialise() { + CommonOptions common("", "", QPIDC_CONF_FILE); qpid::ModuleOptions moduleOptions(QPIDC_MODULE_DIR); string defaultPath (moduleOptions.loadDir); - moduleOptions.parse (0, 0, QPIDC_CONF_FILE, true); - + common.parse(0, 0, common.clientConfig, true); + moduleOptions.parse (0, 0, common.clientConfig, true); + for (vector<string>::iterator iter = moduleOptions.load.begin(); iter != moduleOptions.load.end(); iter++) diff --git a/cpp/src/qpid/client/SessionImpl.cpp b/cpp/src/qpid/client/SessionImpl.cpp index 9ac5323a53..3f3ad617f4 100644 --- a/cpp/src/qpid/client/SessionImpl.cpp +++ b/cpp/src/qpid/client/SessionImpl.cpp @@ -324,7 +324,7 @@ struct MethodContentAdaptor : MethodContent MethodContentAdaptor(const FrameSet& f) : header(*f.getHeaders()), content(f.getContent()) {} - AMQHeaderBody getHeader() const + const AMQHeaderBody& getHeader() const { return header; } diff --git a/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp index 6b6bf884ec..ab0c5c4957 100644 --- a/cpp/src/qpid/client/SslConnector.cpp +++ b/cpp/src/qpid/client/SslConnector.cpp @@ -148,8 +148,10 @@ namespace { struct StaticInit { StaticInit() { try { + CommonOptions common("", "", QPIDC_CONF_FILE); SslOptions options; - options.parse (0, 0, QPIDC_CONF_FILE, true); + common.parse(0, 0, common.clientConfig, true); + options.parse (0, 0, common.clientConfig, true); if (options.certDbPath.empty()) { QPID_LOG(info, "SSL connector not enabled, you must set QPID_SSL_CERT_DB to enable it."); } else { diff --git a/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp index 51eacf77e8..4660a41c07 100644 --- a/cpp/src/qpid/client/TCPConnector.cpp +++ b/cpp/src/qpid/client/TCPConnector.cpp @@ -97,7 +97,7 @@ void TCPConnector::connect(const std::string& host, const std::string& port) { boost::bind(&TCPConnector::connected, this, _1), boost::bind(&TCPConnector::connectFailed, this, _3)); closed = false; - identifier = str(format("[%1%]") % socket.getFullAddress()); + connector->start(poller); } @@ -120,6 +120,8 @@ void TCPConnector::start(sys::AsynchIO* aio_) { for (int i = 0; i < 4; i++) { aio->queueReadBuffer(new Buff(maxFrameSize)); } + + identifier = str(format("[%1%]") % socket.getFullAddress()); } void TCPConnector::initAmqp() { @@ -129,7 +131,7 @@ void TCPConnector::initAmqp() { void TCPConnector::connectFailed(const std::string& msg) { connector = 0; - QPID_LOG(warning, "Connect failed: " << msg << " " << identifier); + QPID_LOG(warning, "Connect failed: " << msg); socket.close(); if (!closed) closed = true; @@ -183,7 +185,7 @@ sys::ShutdownHandler* TCPConnector::getShutdownHandler() const { return shutdownHandler; } -const std::string& TCPConnector::getIdentifier() const { +const std::string& TCPConnector::getIdentifier() const { return identifier; } diff --git a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp index 5924e30dd8..a8f4fb5237 100644 --- a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp +++ b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -32,6 +32,7 @@ #include "qpid/framing/ExchangeBoundResult.h" #include "qpid/framing/ExchangeQueryResult.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/QueueQueryResult.h" #include "qpid/framing/ReplyTo.h" #include "qpid/framing/reply_exceptions.h" diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp index 3cfd2e37f2..2ea4dc0c61 100644 --- a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp +++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -41,6 +41,7 @@ using qpid::framing::Uuid; namespace { +const std::string TCP("tcp"); double FOREVER(std::numeric_limits<double>::max()); // Time values in seconds can be specified as integer or floating point values. @@ -290,7 +291,7 @@ bool ConnectionImpl::tryConnect() for (std::vector<std::string>::const_iterator i = urls.begin(); i != urls.end(); ++i) { try { QPID_LOG(info, "Trying to connect to " << *i << "..."); - Url url(*i); + Url url(*i, settings.protocol.size() ? settings.protocol : TCP); if (url.getUser().size()) settings.username = url.getUser(); if (url.getPass().size()) settings.password = url.getPass(); connection.open(url, settings); diff --git a/cpp/src/qpid/cluster/Cluster.cpp b/cpp/src/qpid/cluster/Cluster.cpp index 3c1d23c842..34aaf3d341 100644 --- a/cpp/src/qpid/cluster/Cluster.cpp +++ b/cpp/src/qpid/cluster/Cluster.cpp @@ -131,6 +131,7 @@ #include "qpid/cluster/UpdateExchange.h" #include "qpid/cluster/ClusterTimer.h" #include "qpid/cluster/CredentialsExchange.h" +#include "qpid/cluster/UpdateClient.h" #include "qpid/assert.h" #include "qmf/org/apache/qpid/cluster/ArgsClusterStopClusterNode.h" @@ -202,7 +203,7 @@ namespace arg=client::arg; * Currently use SVN revision to avoid clashes with versions from * different branches. */ -const uint32_t Cluster::CLUSTER_VERSION = 1207877; +const uint32_t Cluster::CLUSTER_VERSION = 1332342; struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler { qpid::cluster::Cluster& cluster; @@ -269,7 +270,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : "Error delivering frames", poller), failoverExchange(new FailoverExchange(broker.GetVhostObject(), &broker)), - updateDataExchange(new UpdateDataExchange(*this)), credentialsExchange(new CredentialsExchange(*this)), quorum(boost::bind(&Cluster::leave, this)), decoder(boost::bind(&Cluster::deliverFrame, this, _1)), @@ -295,15 +295,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : // Failover exchange provides membership updates to clients. broker.getExchanges().registerExchange(failoverExchange); - // Update exchange is used during updates to replicate messages - // without modifying delivery-properties.exchange. - broker.getExchanges().registerExchange( - boost::shared_ptr<broker::Exchange>(new UpdateExchange(this))); - - // Update-data exchange is used for passing data that may be too large - // for single control frame. - broker.getExchanges().registerExchange(updateDataExchange); - // CredentialsExchange is used to authenticate new cluster members broker.getExchanges().registerExchange(credentialsExchange); @@ -680,6 +671,17 @@ void Cluster::initMapCompleted(Lock& l) { authenticate(); broker.setRecovery(false); // Ditch my current store. broker.setClusterUpdatee(true); + + // Update exchange is used during updates to replicate messages + // without modifying delivery-properties.exchange. + broker.getExchanges().registerExchange( + boost::shared_ptr<broker::Exchange>(new UpdateExchange(this))); + + // Update-data exchange is used during update for passing data that + // may be too large for single control frame. + updateDataExchange.reset(new UpdateDataExchange(*this)); + broker.getExchanges().registerExchange(updateDataExchange); + if (mAgent) mAgent->suppress(true); // Suppress mgmt output during update. state = JOINER; mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self); @@ -999,6 +1001,10 @@ void Cluster::checkUpdateIn(Lock& l) { boost::ref(broker.getExchanges()))); enableClusterSafe(); // Enable cluster-safe assertions deliverEventQueue.start(); + // FIXME aconway 2012-04-04: unregister/delete Update[Data]Exchange + updateDataExchange.reset(); + broker.getExchanges().destroy(UpdateDataExchange::EXCHANGE_NAME); + broker.getExchanges().destroy(UpdateClient::UPDATE); } else if (updateRetracted) { // Update was retracted, request another update updateRetracted = false; diff --git a/cpp/src/qpid/cluster/ClusterMap.cpp b/cpp/src/qpid/cluster/ClusterMap.cpp index a8389095c9..d9817db35f 100644 --- a/cpp/src/qpid/cluster/ClusterMap.cpp +++ b/cpp/src/qpid/cluster/ClusterMap.cpp @@ -21,6 +21,7 @@ #include "qpid/cluster/ClusterMap.h" #include "qpid/Url.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include <boost/bind.hpp> #include <algorithm> @@ -29,7 +30,8 @@ #include <ostream> using namespace std; -using namespace boost; +using boost::ref; +using boost::optional; namespace qpid { using namespace framing; diff --git a/cpp/src/qpid/cluster/ClusterTimer.cpp b/cpp/src/qpid/cluster/ClusterTimer.cpp index b4f7d00f38..90e4fa9d4d 100644 --- a/cpp/src/qpid/cluster/ClusterTimer.cpp +++ b/cpp/src/qpid/cluster/ClusterTimer.cpp @@ -24,6 +24,7 @@ #include "qpid/log/Statement.h" #include "qpid/framing/ClusterTimerWakeupBody.h" #include "qpid/framing/ClusterTimerDropBody.h" +#include "qpid/sys/ClusterSafe.h" namespace qpid { namespace cluster { @@ -107,6 +108,7 @@ void ClusterTimer::drop(intrusive_ptr<TimerTask> t) { // Deliver thread void ClusterTimer::deliverWakeup(const std::string& name) { QPID_LOG(trace, "Cluster timer wakeup delivered for " << name); + qpid::sys::assertClusterSafe(); Map::iterator i = map.find(name); if (i == map.end()) throw Exception(QPID_MSG("Cluster timer wakeup non-existent task " << name)); diff --git a/cpp/src/qpid/cluster/Connection.cpp b/cpp/src/qpid/cluster/Connection.cpp index fc6ada096f..512e0f03cb 100644 --- a/cpp/src/qpid/cluster/Connection.cpp +++ b/cpp/src/qpid/cluster/Connection.cpp @@ -47,6 +47,7 @@ #include "qpid/framing/ClusterConnectionAnnounceBody.h" #include "qpid/framing/ConnectionCloseBody.h" #include "qpid/framing/ConnectionCloseOkBody.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include "qpid/sys/ClusterSafe.h" #include "qpid/types/Variant.h" @@ -796,6 +797,54 @@ void Connection::config(const std::string& encoded) { else throw Exception(QPID_MSG("Update failed, invalid kind of config: " << kind)); } +namespace { + // find a Link that matches the given Address + class LinkFinder { + qpid::Address id; + boost::shared_ptr<broker::Link> link; + public: + LinkFinder(const qpid::Address& _id) : id(_id) {} + boost::shared_ptr<broker::Link> getLink() { return link; } + void operator() (boost::shared_ptr<broker::Link> l) + { + if (!link) { + qpid::Address addr(l->getTransport(), l->getHost(), l->getPort()); + if (id == addr) { + link = l; + } + } + } + }; +} + +void Connection::internalState(const std::string& type, + const std::string& name, + const framing::FieldTable& state) +{ + if (type == "link") { + // name is the string representation of the Link's _configured_ destination address + Url dest; + try { + dest = name; + } catch(...) { + throw Exception(QPID_MSG("Update failed, invalid format for Link destination address: " << name)); + } + assert(dest.size()); + LinkFinder finder(dest[0]); + cluster.getBroker().getLinks().eachLink(boost::ref(finder)); + if (finder.getLink()) { + try { + finder.getLink()->setState(state); + } catch(...) { + throw Exception(QPID_MSG("Update failed, invalid state for Link " << name << ", state: " << state)); + } + QPID_LOG(debug, cluster << " updated link " << dest[0] << " with state: " << state); + } else throw Exception(QPID_MSG("Update failed, unable to find Link named: " << name)); + } + else throw Exception(QPID_MSG("Update failed, invalid object type for internal state replication: " << type)); +} + + void Connection::doCatchupIoCallbacks() { // We need to process IO callbacks during the catch-up phase in // order to service asynchronous completions for messages diff --git a/cpp/src/qpid/cluster/Connection.h b/cpp/src/qpid/cluster/Connection.h index 920c4937db..26514c76e2 100644 --- a/cpp/src/qpid/cluster/Connection.h +++ b/cpp/src/qpid/cluster/Connection.h @@ -200,6 +200,8 @@ class Connection : const std::string& instance); void config(const std::string& encoded); + void internalState(const std::string& type, const std::string& name, + const framing::FieldTable& state); void setSecureConnection ( broker::SecureConnection * sc ); diff --git a/cpp/src/qpid/cluster/CredentialsExchange.cpp b/cpp/src/qpid/cluster/CredentialsExchange.cpp index 0fafc521cd..416a3636e9 100644 --- a/cpp/src/qpid/cluster/CredentialsExchange.cpp +++ b/cpp/src/qpid/cluster/CredentialsExchange.cpp @@ -62,7 +62,8 @@ bool CredentialsExchange::check(MemberId member) { return valid; } -void CredentialsExchange::route(broker::Deliverable& msg, const string& /*routingKey*/, const framing::FieldTable* args) { +void CredentialsExchange::route(broker::Deliverable& msg) { + const framing::FieldTable* args = msg.getMessage().getApplicationHeaders(); sys::Mutex::ScopedLock l(lock); const broker::ConnectionState* connection = static_cast<const broker::ConnectionState*>(msg.getMessage().getPublisher()); diff --git a/cpp/src/qpid/cluster/CredentialsExchange.h b/cpp/src/qpid/cluster/CredentialsExchange.h index 90fd188271..74cf8350a6 100644 --- a/cpp/src/qpid/cluster/CredentialsExchange.h +++ b/cpp/src/qpid/cluster/CredentialsExchange.h @@ -50,7 +50,7 @@ class CredentialsExchange : public broker::Exchange bool check(MemberId member); /** Throw an exception if the calling connection is not the cluster user. Store credentials in msg. */ - void route(broker::Deliverable& msg, const std::string& routingKey, const framing::FieldTable* args); + void route(broker::Deliverable& msg); // Exchange overrides std::string getType() const; diff --git a/cpp/src/qpid/cluster/FailoverExchange.cpp b/cpp/src/qpid/cluster/FailoverExchange.cpp index 43ec27cf2c..87202a887c 100644 --- a/cpp/src/qpid/cluster/FailoverExchange.cpp +++ b/cpp/src/qpid/cluster/FailoverExchange.cpp @@ -80,7 +80,7 @@ bool FailoverExchange::isBound(Queue::shared_ptr queue, const string* const, con return queues.find(queue) != queues.end(); } -void FailoverExchange::route(Deliverable&, const string& , const framing::FieldTable* ) { +void FailoverExchange::route(Deliverable&) { QPID_LOG(warning, "Message received by exchange " << typeName << " ignoring"); } diff --git a/cpp/src/qpid/cluster/FailoverExchange.h b/cpp/src/qpid/cluster/FailoverExchange.h index c3e50c6929..5ac734a7ac 100644 --- a/cpp/src/qpid/cluster/FailoverExchange.h +++ b/cpp/src/qpid/cluster/FailoverExchange.h @@ -54,7 +54,7 @@ class FailoverExchange : public broker::Exchange bool bind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const framing::FieldTable* args); bool unbind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const framing::FieldTable* args); bool isBound(boost::shared_ptr<broker::Queue> queue, const std::string* const routingKey, const framing::FieldTable* const args); - void route(broker::Deliverable& msg, const std::string& routingKey, const framing::FieldTable* args); + void route(broker::Deliverable& msg); private: void sendUpdate(const boost::shared_ptr<broker::Queue>&); diff --git a/cpp/src/qpid/cluster/InitialStatusMap.cpp b/cpp/src/qpid/cluster/InitialStatusMap.cpp index eb65005a9e..fc53d1076b 100644 --- a/cpp/src/qpid/cluster/InitialStatusMap.cpp +++ b/cpp/src/qpid/cluster/InitialStatusMap.cpp @@ -30,9 +30,9 @@ namespace qpid { namespace cluster { using namespace std; -using namespace boost; using namespace framing::cluster; using namespace framing; +using boost::optional; InitialStatusMap::InitialStatusMap(const MemberId& self_, size_t size_) : self(self_), completed(), resendNeeded(), size(size_) diff --git a/cpp/src/qpid/cluster/UpdateClient.cpp b/cpp/src/qpid/cluster/UpdateClient.cpp index 95c64ff060..20684fd8a7 100644 --- a/cpp/src/qpid/cluster/UpdateClient.cpp +++ b/cpp/src/qpid/cluster/UpdateClient.cpp @@ -57,6 +57,7 @@ #include "qpid/framing/ClusterConnectionShadowReadyBody.h" #include "qpid/framing/ClusterConnectionSessionStateBody.h" #include "qpid/framing/ClusterConnectionConsumerStateBody.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/enum.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/framing/TypeCode.h" @@ -687,7 +688,15 @@ void UpdateClient::updateLinks() { void UpdateClient::updateLink(const boost::shared_ptr<broker::Link>& link) { QPID_LOG(debug, *this << " updating link " << link->getHost() << ":" << link->getPort()); - ClusterConnectionProxy(session).config(encode(*link)); + ClusterConnectionProxy(session).config(encode(*link)); // push the configuration + // now push the current state + framing::FieldTable state; + link->getState(state); + std::ostringstream os; + os << qpid::Address(link->getTransport(), link->getHost(), link->getPort()); + ClusterConnectionProxy(session).internalState(std::string("link"), + os.str(), + state); } void UpdateClient::updateBridge(const boost::shared_ptr<broker::Bridge>& bridge) { diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.cpp b/cpp/src/qpid/cluster/UpdateDataExchange.cpp index e5cd82e3d3..31d96c67ca 100644 --- a/cpp/src/qpid/cluster/UpdateDataExchange.cpp +++ b/cpp/src/qpid/cluster/UpdateDataExchange.cpp @@ -40,9 +40,9 @@ UpdateDataExchange::UpdateDataExchange(Cluster& cluster) : Exchange(EXCHANGE_NAME, &cluster) {} -void UpdateDataExchange::route(broker::Deliverable& msg, const std::string& routingKey, - const qpid::framing::FieldTable* ) +void UpdateDataExchange::route(broker::Deliverable& msg) { + const std::string& routingKey = msg.getMessage().getRoutingKey(); std::string data = msg.getMessage().getFrames().getContent(); if (routingKey == MANAGEMENT_AGENTS_KEY) managementAgents = data; else if (routingKey == MANAGEMENT_SCHEMAS_KEY) managementSchemas = data; diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.h b/cpp/src/qpid/cluster/UpdateDataExchange.h index d2f6c35ad0..f79430f111 100644 --- a/cpp/src/qpid/cluster/UpdateDataExchange.h +++ b/cpp/src/qpid/cluster/UpdateDataExchange.h @@ -50,8 +50,7 @@ class UpdateDataExchange : public broker::Exchange UpdateDataExchange(Cluster& parent); - void route(broker::Deliverable& msg, const std::string& routingKey, - const framing::FieldTable* args); + void route(broker::Deliverable& msg); // Not implemented std::string getType() const { return EXCHANGE_TYPE; } diff --git a/cpp/src/qpid/framing/AMQFrame.h b/cpp/src/qpid/framing/AMQFrame.h index 4f6faf4199..19675ce6ff 100644 --- a/cpp/src/qpid/framing/AMQFrame.h +++ b/cpp/src/qpid/framing/AMQFrame.h @@ -43,8 +43,7 @@ class QPID_COMMON_CLASS_EXTERN AMQFrame : public AMQDataBlock ChannelId getChannel() const { return channel; } void setChannel(ChannelId c) { channel = c; } - AMQBody* getBody() { return body.get(); } - const AMQBody* getBody() const { return body.get(); } + AMQBody* getBody() const { return body.get(); } AMQMethodBody* getMethod() { return getBody() ? getBody()->getMethod() : 0; } const AMQMethodBody* getMethod() const { return getBody() ? getBody()->getMethod() : 0; } diff --git a/cpp/src/qpid/framing/BodyHandler.cpp b/cpp/src/qpid/framing/BodyHandler.cpp deleted file mode 100644 index db302b1e4c..0000000000 --- a/cpp/src/qpid/framing/BodyHandler.cpp +++ /dev/null @@ -1,56 +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/framing/BodyHandler.h" -#include "qpid/framing/AMQMethodBody.h" -#include "qpid/framing/AMQHeaderBody.h" -#include "qpid/framing/AMQContentBody.h" -#include "qpid/framing/AMQHeartbeatBody.h" -#include <boost/cast.hpp> -#include "qpid/framing/reply_exceptions.h" -#include "qpid/Msg.h" - -using namespace qpid::framing; -using namespace boost; - -BodyHandler::~BodyHandler() {} - -// TODO aconway 2007-08-13: Replace with visitor. -void BodyHandler::handleBody(AMQBody* body) { - switch(body->type()) - { - case METHOD_BODY: - handleMethod(polymorphic_downcast<AMQMethodBody*>(body)); - break; - case HEADER_BODY: - handleHeader(polymorphic_downcast<AMQHeaderBody*>(body)); - break; - case CONTENT_BODY: - handleContent(polymorphic_downcast<AMQContentBody*>(body)); - break; - case HEARTBEAT_BODY: - handleHeartbeat(polymorphic_downcast<AMQHeartbeatBody*>(body)); - break; - default: - throw FramingErrorException( - QPID_MSG("Invalid frame type " << body->type())); - } -} - diff --git a/cpp/src/qpid/framing/BodyHandler.h b/cpp/src/qpid/framing/BodyHandler.h deleted file mode 100644 index 9ded737195..0000000000 --- a/cpp/src/qpid/framing/BodyHandler.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _BodyHandler_ -#define _BodyHandler_ - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include <boost/shared_ptr.hpp> - -namespace qpid { -namespace framing { -class AMQBody; -class AMQMethodBody; -class AMQHeaderBody; -class AMQContentBody; -class AMQHeartbeatBody; - -// TODO aconway 2007-08-10: rework using Visitor pattern? - -/** - * Interface to handle incoming frame bodies. - * Derived classes provide logic for each frame type. - */ -class BodyHandler { - public: - virtual ~BodyHandler(); - virtual void handleBody(AMQBody* body); - - protected: - virtual void handleMethod(AMQMethodBody*) = 0; - virtual void handleHeader(AMQHeaderBody*) = 0; - virtual void handleContent(AMQContentBody*) = 0; - virtual void handleHeartbeat(AMQHeartbeatBody*) = 0; -}; - -}} - - -#endif diff --git a/cpp/src/qpid/framing/FieldTable.cpp b/cpp/src/qpid/framing/FieldTable.cpp index f80d2f9fb1..0f7140b627 100644 --- a/cpp/src/qpid/framing/FieldTable.cpp +++ b/cpp/src/qpid/framing/FieldTable.cpp @@ -28,29 +28,93 @@ #include "qpid/Msg.h" #include <assert.h> +// The locking rationale in the FieldTable seems a little odd, but it +// maintains the concurrent guarantees and requirements that were in +// place before the cachedBytes/cachedSize were added: +// +// The FieldTable client code needs to make sure that they call no write +// operation in parallel with any other operation on the FieldTable. +// However multiple parallel read operations are safe. +// +// To this end the only code that is locked is code that can transparently +// change the state of the FieldTable during a read only operation. +// (In other words the code that required the mutable members in the class +// definition!) +// namespace qpid { + +using sys::Mutex; +using sys::ScopedLock; + namespace framing { +FieldTable::FieldTable() : + cachedSize(0), + newBytes(false) +{ +} + FieldTable::FieldTable(const FieldTable& ft) { - *this = ft; + ScopedLock<Mutex> l(ft.lock); // lock _source_ FieldTable + + cachedBytes = ft.cachedBytes; + cachedSize = ft.cachedSize; + newBytes = ft.newBytes; + + // Only copy the values if we have no raw data + // - copying the map is expensive and we can + // reconstruct it if necessary from the raw data + if (cachedBytes) { + newBytes = true; + return; + } + // In practice Encoding the source field table and only copying + // the encoded bytes is faster than copying the whole value map. + // (Because we nearly always copy a field table internally before + // encoding it to send, but don't change it after the copy) + if (!ft.values.empty()) { + // Side effect of getting encoded size will cache it in ft.cachedSize + ft.cachedBytes = boost::shared_array<uint8_t>(new uint8_t[ft.encodedSize()]); + + Buffer buffer((char*)&ft.cachedBytes[0], ft.cachedSize); + + // Cut and paste ahead... + buffer.putLong(ft.encodedSize() - 4); + buffer.putLong(ft.values.size()); + for (ValueMap::const_iterator i = ft.values.begin(); i!=ft.values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } + + cachedBytes = ft.cachedBytes; + cachedSize = ft.cachedSize; + newBytes = true; + } } FieldTable& FieldTable::operator=(const FieldTable& ft) { - clear(); - values = ft.values; - return *this; + FieldTable nft(ft); + values.swap(nft.values); + cachedBytes.swap(nft.cachedBytes); + cachedSize = nft.cachedSize; + newBytes = nft.newBytes; + return (*this); } -FieldTable::~FieldTable() {} - uint32_t FieldTable::encodedSize() const { + ScopedLock<Mutex> l(lock); + + if (cachedSize != 0) { + return cachedSize; + } uint32_t len(4/*size field*/ + 4/*count field*/); for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { // shortstr_len_byte + key size + value size - len += 1 + (i->first).size() + (i->second)->encodedSize(); + len += 1 + (i->first).size() + (i->second)->encodedSize(); } + cachedSize = len; return len; } @@ -66,6 +130,7 @@ std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_ty } std::ostream& operator<<(std::ostream& out, const FieldTable& t) { + t.realDecode(); out << "{"; FieldTable::ValueMap::const_iterator i = t.begin(); if (i != t.end()) out << *i++; @@ -77,48 +142,70 @@ std::ostream& operator<<(std::ostream& out, const FieldTable& t) { } void FieldTable::set(const std::string& name, const ValuePtr& value){ + realDecode(); values[name] = value; + flushRawCache(); } void FieldTable::setString(const std::string& name, const std::string& value){ + realDecode(); values[name] = ValuePtr(new Str16Value(value)); + flushRawCache(); } void FieldTable::setInt(const std::string& name, const int value){ + realDecode(); values[name] = ValuePtr(new IntegerValue(value)); + flushRawCache(); } void FieldTable::setInt64(const std::string& name, const int64_t value){ + realDecode(); values[name] = ValuePtr(new Integer64Value(value)); + flushRawCache(); } void FieldTable::setTimestamp(const std::string& name, const uint64_t value){ + realDecode(); values[name] = ValuePtr(new TimeValue(value)); + flushRawCache(); } void FieldTable::setUInt64(const std::string& name, const uint64_t value){ + realDecode(); values[name] = ValuePtr(new Unsigned64Value(value)); + flushRawCache(); } void FieldTable::setTable(const std::string& name, const FieldTable& value) { + realDecode(); values[name] = ValuePtr(new FieldTableValue(value)); + flushRawCache(); } void FieldTable::setArray(const std::string& name, const Array& value) { + realDecode(); values[name] = ValuePtr(new ArrayValue(value)); + flushRawCache(); } void FieldTable::setFloat(const std::string& name, const float value){ + realDecode(); values[name] = ValuePtr(new FloatValue(value)); + flushRawCache(); } void FieldTable::setDouble(const std::string& name, double value){ + realDecode(); values[name] = ValuePtr(new DoubleValue(value)); + flushRawCache(); } FieldTable::ValuePtr FieldTable::get(const std::string& name) const { + // Ensure we have any values we're trying to read + realDecode(); ValuePtr value; ValueMap::const_iterator i = values.find(name); if ( i!=values.end() ) @@ -188,37 +275,82 @@ bool FieldTable::getDouble(const std::string& name, double& value) const { //} void FieldTable::encode(Buffer& buffer) const { - buffer.putLong(encodedSize() - 4); - buffer.putLong(values.size()); - for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { - buffer.putShortString(i->first); - i->second->encode(buffer); + // If we've still got the input field table + // we can just copy it directly to the output + if (cachedBytes) { + ScopedLock<Mutex> l(lock); + buffer.putRawData(&cachedBytes[0], cachedSize); + } else { + buffer.putLong(encodedSize() - 4); + buffer.putLong(values.size()); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } } } +// Decode lazily - just record the raw bytes until we need them void FieldTable::decode(Buffer& buffer){ - clear(); if (buffer.available() < 4) throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + uint32_t p = buffer.getPosition(); uint32_t len = buffer.getLong(); if (len) { uint32_t available = buffer.available(); if ((available < len) || (available < 4)) throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + } + ScopedLock<Mutex> l(lock); + // Throw away previous stored values + values.clear(); + // Copy data into our buffer + cachedBytes = boost::shared_array<uint8_t>(new uint8_t[len + 4]); + cachedSize = len + 4; + newBytes = true; + buffer.setPosition(p); + buffer.getRawData(&cachedBytes[0], cachedSize); +} + +void FieldTable::realDecode() const +{ + ScopedLock<Mutex> l(lock); + + // If we've got no raw data stored up then nothing to do + if (!newBytes) + return; + + Buffer buffer((char*)&cachedBytes[0], cachedSize); + uint32_t len = buffer.getLong(); + if (len) { + uint32_t available = buffer.available(); uint32_t count = buffer.getLong(); uint32_t leftover = available - len; while(buffer.available() > leftover && count--){ std::string name; ValuePtr value(new FieldValue); - + buffer.getShortString(name); value->decode(buffer); values[name] = ValuePtr(value); - } + } } + newBytes = false; +} + +void FieldTable::flushRawCache() +{ + ScopedLock<Mutex> l(lock); + // We can only flush the cache if there are no cached bytes to decode + assert(newBytes==false); + // Avoid recreating shared array unless we actually have one. + if (cachedBytes) cachedBytes.reset(); + cachedSize = 0; } bool FieldTable::operator==(const FieldTable& x) const { + realDecode(); + x.realDecode(); if (values.size() != x.values.size()) return false; for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { ValueMap::const_iterator j = x.values.find(i->first); @@ -230,20 +362,73 @@ bool FieldTable::operator==(const FieldTable& x) const { void FieldTable::erase(const std::string& name) { - if (values.find(name) != values.end()) + realDecode(); + if (values.find(name) != values.end()) { values.erase(name); + flushRawCache(); + } +} + +void FieldTable::clear() +{ + values.clear(); + newBytes = false; + flushRawCache(); +} + +// Map-like interface. +FieldTable::ValueMap::const_iterator FieldTable::begin() const +{ + realDecode(); + return values.begin(); +} + +FieldTable::ValueMap::const_iterator FieldTable::end() const +{ + realDecode(); + return values.end(); +} + +FieldTable::ValueMap::const_iterator FieldTable::find(const std::string& s) const +{ + realDecode(); + return values.find(s); +} + +FieldTable::ValueMap::iterator FieldTable::begin() +{ + realDecode(); + flushRawCache(); + return values.begin(); +} + +FieldTable::ValueMap::iterator FieldTable::end() +{ + realDecode(); + flushRawCache(); + return values.end(); +} + +FieldTable::ValueMap::iterator FieldTable::find(const std::string& s) +{ + realDecode(); + flushRawCache(); + return values.find(s); } std::pair<FieldTable::ValueMap::iterator, bool> FieldTable::insert(const ValueMap::value_type& value) { + realDecode(); + flushRawCache(); return values.insert(value); } FieldTable::ValueMap::iterator FieldTable::insert(ValueMap::iterator position, const ValueMap::value_type& value) { + realDecode(); + flushRawCache(); return values.insert(position, value); } - } } diff --git a/cpp/src/qpid/framing/FrameSet.cpp b/cpp/src/qpid/framing/FrameSet.cpp index 255aaf6e6b..9aee7b98b9 100644 --- a/cpp/src/qpid/framing/FrameSet.cpp +++ b/cpp/src/qpid/framing/FrameSet.cpp @@ -26,7 +26,6 @@ #include "qpid/framing/TypeFilter.h" using namespace qpid::framing; -using namespace boost; FrameSet::FrameSet(const SequenceNumber& _id) : id(_id),contentSize(0),recalculateSize(true) { } FrameSet::FrameSet(const FrameSet& original) : id(original.id), contentSize(0), recalculateSize(true) @@ -103,3 +102,7 @@ std::string FrameSet::getContent() const { getContent(out); return out; } + +bool FrameSet::hasContent() const { + return parts.size() >= 3; +} diff --git a/cpp/src/qpid/framing/FrameSet.h b/cpp/src/qpid/framing/FrameSet.h index cae75e5ec8..3b9f60950b 100644 --- a/cpp/src/qpid/framing/FrameSet.h +++ b/cpp/src/qpid/framing/FrameSet.h @@ -54,6 +54,7 @@ public: QPID_COMMON_EXTERN void getContent(std::string&) const; QPID_COMMON_EXTERN std::string getContent() const; + QPID_COMMON_EXTERN bool hasContent() const; bool isContentBearing() const; diff --git a/cpp/src/qpid/framing/MethodContent.h b/cpp/src/qpid/framing/MethodContent.h index b290a0c140..58c9143cfa 100644 --- a/cpp/src/qpid/framing/MethodContent.h +++ b/cpp/src/qpid/framing/MethodContent.h @@ -32,7 +32,7 @@ class MethodContent public: virtual ~MethodContent() {} //TODO: rethink this interface - virtual AMQHeaderBody getHeader() const = 0; + virtual const AMQHeaderBody& getHeader() const = 0; virtual const std::string& getData() const = 0; }; diff --git a/cpp/src/qpid/framing/TransferContent.cpp b/cpp/src/qpid/framing/TransferContent.cpp index 837d7d346a..d997b24304 100644 --- a/cpp/src/qpid/framing/TransferContent.cpp +++ b/cpp/src/qpid/framing/TransferContent.cpp @@ -30,7 +30,7 @@ TransferContent::TransferContent(const std::string& data, const std::string& key } -AMQHeaderBody TransferContent::getHeader() const +const AMQHeaderBody& TransferContent::getHeader() const { return header; } diff --git a/cpp/src/qpid/framing/TransferContent.h b/cpp/src/qpid/framing/TransferContent.h index 9a698a1823..32663d7020 100644 --- a/cpp/src/qpid/framing/TransferContent.h +++ b/cpp/src/qpid/framing/TransferContent.h @@ -40,7 +40,7 @@ public: QPID_COMMON_EXTERN TransferContent(const std::string& data = std::string(), const std::string& key=std::string()); ///@internal - QPID_COMMON_EXTERN AMQHeaderBody getHeader() const; + QPID_COMMON_EXTERN const AMQHeaderBody& getHeader() const; QPID_COMMON_EXTERN void setData(const std::string&); QPID_COMMON_EXTERN const std::string& getData() const; diff --git a/cpp/src/qpid/framing/amqp_framing.h b/cpp/src/qpid/framing/amqp_framing.h index 3a8b39afb5..2e58922364 100644 --- a/cpp/src/qpid/framing/amqp_framing.h +++ b/cpp/src/qpid/framing/amqp_framing.h @@ -21,7 +21,6 @@ #include "qpid/framing/amqp_types.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/AMQBody.h" -#include "qpid/framing/BodyHandler.h" #include "qpid/framing/AMQMethodBody.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/framing/AMQContentBody.h" diff --git a/cpp/src/qpid/ha/Backup.cpp b/cpp/src/qpid/ha/Backup.cpp index 5acbfb9d5f..3f3fa87a01 100644 --- a/cpp/src/qpid/ha/Backup.cpp +++ b/cpp/src/qpid/ha/Backup.cpp @@ -19,10 +19,11 @@ * */ #include "Backup.h" -#include "Settings.h" #include "BrokerReplicator.h" -#include "ReplicatingSubscription.h" #include "ConnectionExcluder.h" +#include "HaBroker.h" +#include "ReplicatingSubscription.h" +#include "Settings.h" #include "qpid/Url.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/broker/Bridge.h" @@ -43,37 +44,44 @@ using namespace broker; using types::Variant; using std::string; -Backup::Backup(broker::Broker& b, const Settings& s) : - broker(b), settings(s), excluder(new ConnectionExcluder()) +Backup::Backup(HaBroker& hb, const Settings& s) : + haBroker(hb), broker(hb.getBroker()), settings(s), excluder(new ConnectionExcluder()) { + // Exclude client connections before starting the link to avoid self-connection. + broker.getConnectionObservers().add(excluder); // Empty brokerUrl means delay initialization until setUrl() is called. if (!s.brokerUrl.empty()) initialize(Url(s.brokerUrl)); } void Backup::initialize(const Url& url) { - assert(!url.empty()); - QPID_LOG(notice, "Ha: Backup started: " << url); + if (url.empty()) throw Url::Invalid("HA broker URL is empty"); + QPID_LOG(notice, "HA: Backup initialized: " << url); string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol; // Declare the link std::pair<Link::shared_ptr, bool> result = broker.getLinks().declare( url[0].host, url[0].port, protocol, false, // durable settings.mechanism, settings.username, settings.password); - assert(result.second); // FIXME aconway 2011-11-23: error handling link = result.first; link->setUrl(url); - - replicator.reset(new BrokerReplicator(link)); + replicator.reset(new BrokerReplicator(haBroker, link)); broker.getExchanges().registerExchange(replicator); - broker.getConnectionObservers().add(excluder); } +Backup::~Backup() { + if (link) link->close(); + if (replicator.get()) broker.getExchanges().destroy(replicator->getName()); + replicator.reset(); + broker.getConnectionObservers().remove(excluder); // This allows client connections. +} + + void Backup::setBrokerUrl(const Url& url) { // Ignore empty URLs seen during start-up for some tests. if (url.empty()) return; sys::Mutex::ScopedLock l(lock); if (link) { // URL changed after we initialized. - QPID_LOG(info, "HA: Backup failover URL set to " << url); + QPID_LOG(info, "HA: Backup broker URL set to " << url); link->setUrl(url); } else { @@ -81,10 +89,4 @@ void Backup::setBrokerUrl(const Url& url) { } } -Backup::~Backup() { - if (link) link->close(); - if (replicator.get()) broker.getExchanges().destroy(replicator->getName()); - broker.getConnectionObservers().remove(excluder); // This allows client connections. -} - }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/Backup.h b/cpp/src/qpid/ha/Backup.h index 526b238b82..6c36996914 100644 --- a/cpp/src/qpid/ha/Backup.h +++ b/cpp/src/qpid/ha/Backup.h @@ -38,6 +38,7 @@ namespace ha { class Settings; class ConnectionExcluder; class BrokerReplicator; +class HaBroker; /** * State associated with a backup broker. Manages connections to primary. @@ -47,7 +48,7 @@ class BrokerReplicator; class Backup { public: - Backup(broker::Broker&, const Settings&); + Backup(HaBroker&, const Settings&); ~Backup(); void setBrokerUrl(const Url&); @@ -55,6 +56,7 @@ class Backup void initialize(const Url&); sys::Mutex lock; + HaBroker& haBroker; broker::Broker& broker; Settings settings; boost::shared_ptr<broker::Link> link; diff --git a/cpp/src/qpid/ha/BrokerReplicator.cpp b/cpp/src/qpid/ha/BrokerReplicator.cpp index a8f05c1fe3..d0c99cbdb6 100644 --- a/cpp/src/qpid/ha/BrokerReplicator.cpp +++ b/cpp/src/qpid/ha/BrokerReplicator.cpp @@ -19,6 +19,7 @@ * */ #include "BrokerReplicator.h" +#include "HaBroker.h" #include "QueueReplicator.h" #include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" @@ -37,6 +38,7 @@ #include "qmf/org/apache/qpid/broker/EventQueueDelete.h" #include "qmf/org/apache/qpid/broker/EventSubscribe.h" #include <algorithm> +#include <sstream> namespace qpid { namespace ha { @@ -87,6 +89,7 @@ const string QUEUE("queue"); const string RHOST("rhost"); const string TYPE("type"); const string USER("user"); +const string HA_BROKER("habroker"); const string AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER("agent.ind.event.org_apache_qpid_broker.#"); const string QMF2("qmf2"); @@ -100,6 +103,7 @@ const string _PACKAGE_NAME("_package_name"); const string _SCHEMA_ID("_schema_id"); const string OBJECT("OBJECT"); const string ORG_APACHE_QPID_BROKER("org.apache.qpid.broker"); +const string ORG_APACHE_QPID_HA("org.apache.qpid.ha"); const string QMF_DEFAULT_DIRECT("qmf.default.direct"); const string _QUERY_REQUEST("_query_request"); const string BROKER("broker"); @@ -113,36 +117,13 @@ template <class T> bool match(Variant::Map& schema) { return T::match(schema[CLASS_NAME], schema[PACKAGE_NAME]); } -enum ReplicateLevel { RL_NONE=0, RL_CONFIGURATION, RL_MESSAGES }; -const string S_NONE="none"; -const string S_CONFIGURATION="configuration"; -const string S_MESSAGES="messages"; - -ReplicateLevel replicateLevel(const string& level) { - if (level == S_NONE) return RL_NONE; - if (level == S_CONFIGURATION) return RL_CONFIGURATION; - if (level == S_MESSAGES) return RL_MESSAGES; - throw Exception("Invalid value for "+QPID_REPLICATE+": "+level); -} - -ReplicateLevel replicateLevel(const framing::FieldTable& f) { - if (f.isSet(QPID_REPLICATE)) return replicateLevel(f.getAsString(QPID_REPLICATE)); - else return RL_NONE; -} - -ReplicateLevel replicateLevel(const Variant::Map& m) { - Variant::Map::const_iterator i = m.find(QPID_REPLICATE); - if (i != m.end()) return replicateLevel(i->second.asString()); - else return RL_NONE; -} - -void sendQuery(const string className, const string& queueName, SessionHandler& sessionHandler) { +void sendQuery(const string& packageName, const string& className, const string& queueName, SessionHandler& sessionHandler) { framing::AMQP_ServerProxy peer(sessionHandler.out); Variant::Map request; request[_WHAT] = OBJECT; Variant::Map schema; schema[_CLASS_NAME] = className; - schema[_PACKAGE_NAME] = ORG_APACHE_QPID_BROKER; + schema[_PACKAGE_NAME] = packageName; request[_SCHEMA_ID] = schema; AMQFrame method((MessageTransferBody(ProtocolVersion(), QMF_DEFAULT_DIRECT, 0, 0))); @@ -181,13 +162,34 @@ Variant::Map asMapVoid(const Variant& value) { } // namespace + +ReplicateLevel BrokerReplicator::replicateLevel(const std::string& str) { + ReplicateLevel rl; + if (qpid::ha::replicateLevel(str, rl)) return rl; + else return haBroker.getSettings().replicateDefault; +} + +ReplicateLevel BrokerReplicator::replicateLevel(const framing::FieldTable& f) { + if (f.isSet(QPID_REPLICATE)) + return replicateLevel(f.getAsString(QPID_REPLICATE)); + else + return haBroker.getSettings().replicateDefault; +} + +ReplicateLevel BrokerReplicator::replicateLevel(const Variant::Map& m) { + Variant::Map::const_iterator i = m.find(QPID_REPLICATE); + if (i != m.end()) + return replicateLevel(i->second.asString()); + else + return haBroker.getSettings().replicateDefault; +} + BrokerReplicator::~BrokerReplicator() {} -BrokerReplicator::BrokerReplicator(const boost::shared_ptr<Link>& l) - : Exchange(QPID_CONFIGURATION_REPLICATOR), broker(*l->getBroker()), link(l) +BrokerReplicator::BrokerReplicator(HaBroker& hb, const boost::shared_ptr<Link>& l) + : Exchange(QPID_CONFIGURATION_REPLICATOR), + haBroker(hb), broker(hb.getBroker()), link(l) { - QPID_LOG(info, "HA: Backup replicating from " << - link->getTransport() << ":" << link->getHost() << ":" << link->getPort()); broker.getLinks().declare( link->getHost(), link->getPort(), false, // durable @@ -211,22 +213,26 @@ void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionH const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs()); //declare and bind an event queue - peer.getQueue().declare(queueName, "", false, false, true, true, FieldTable()); + FieldTable declareArgs; + declareArgs.setString(QPID_REPLICATE, str(RL_NONE)); + peer.getQueue().declare(queueName, "", false, false, true, true, declareArgs); peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER, FieldTable()); //subscribe to the queue peer.getMessage().subscribe(queueName, args.i_dest, 1, 0, false, "", 0, FieldTable()); peer.getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); peer.getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); - //issue a query request for queues and another for exchanges using event queue as the reply-to address - sendQuery(QUEUE, queueName, sessionHandler); - sendQuery(EXCHANGE, queueName, sessionHandler); - sendQuery(BINDING, queueName, sessionHandler); - QPID_LOG(debug, "HA: Backup activated configuration bridge: " << queueName); + // Issue a query request for queues, exchanges, bindings and the habroker + // using event queue as the reply-to address + sendQuery(ORG_APACHE_QPID_HA, HA_BROKER, queueName, sessionHandler); + sendQuery(ORG_APACHE_QPID_BROKER, QUEUE, queueName, sessionHandler); + sendQuery(ORG_APACHE_QPID_BROKER, EXCHANGE, queueName, sessionHandler); + sendQuery(ORG_APACHE_QPID_BROKER, BINDING, queueName, sessionHandler); + QPID_LOG(debug, "HA: Backup configuration bridge: " << queueName); } -// FIXME aconway 2011-12-02: error handling in route. -void BrokerReplicator::route(Deliverable& msg, const string& /*key*/, const framing::FieldTable* headers) { +void BrokerReplicator::route(Deliverable& msg) { + const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders(); Variant::List list; try { if (!isQMFv2(msg.getMessage()) || !headers) @@ -238,6 +244,7 @@ void BrokerReplicator::route(Deliverable& msg, const string& /*key*/, const fram if (headers->getAsString(QMF_CONTENT) == EVENT) { for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) { Variant::Map& map = i->asMap(); + QPID_LOG(trace, "HA: Backup received event: " << map); Variant::Map& schema = map[SCHEMA_ID].asMap(); Variant::Map& values = map[VALUES].asMap(); if (match<EventQueueDeclare>(schema)) doEventQueueDeclare(values); @@ -249,19 +256,22 @@ void BrokerReplicator::route(Deliverable& msg, const string& /*key*/, const fram } } else if (headers->getAsString(QMF_OPCODE) == QUERY_RESPONSE) { for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) { - string type = i->asMap()[SCHEMA_ID].asMap()[CLASS_NAME]; - Variant::Map& values = i->asMap()[VALUES].asMap(); + Variant::Map& map = i->asMap(); + QPID_LOG(trace, "HA: Backup received event: " << map); + string type = map[SCHEMA_ID].asMap()[CLASS_NAME]; + Variant::Map& values = map[VALUES].asMap(); framing::FieldTable args; amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); if (type == QUEUE) doResponseQueue(values); else if (type == EXCHANGE) doResponseExchange(values); else if (type == BINDING) doResponseBind(values); - else QPID_LOG(error, "HA: Backup received unknown response type=" << type - << " values=" << values); + else if (type == HA_BROKER) doResponseHaBroker(values); } - } else QPID_LOG(error, "HA: Backup received unexpected message: " << *headers); + } } catch (const std::exception& e) { - QPID_LOG(error, "HA: Backup replication error: " << e.what() << ": while handling: " << list); + QPID_LOG(critical, "HA: Backup configuration failed: " << e.what() + << ": while handling: " << list); + throw; } } @@ -282,15 +292,13 @@ void BrokerReplicator::doEventQueueDeclare(Variant::Map& values) { values[USER].asString(), values[RHOST].asString()); if (result.second) { - // FIXME aconway 2011-11-22: should delete old queue and - // re-create from event. - // Events are always up to date, whereas responses may be - // out of date. - QPID_LOG(debug, "HA: Backup created queue: " << name); + QPID_LOG(debug, "HA: Backup queue declare event: " << name); startQueueReplicator(result.first); } else { // FIXME aconway 2011-12-02: what's the right way to handle this? - QPID_LOG(warning, "HA: Backup queue already exists: " << name); + // Should we delete the old & re-create form the event? Responses + // may be old but events are always up-to-date. + QPID_LOG(warning, "HA: Backup queue declare event, already exists: " << name); } } } @@ -300,8 +308,11 @@ void BrokerReplicator::doEventQueueDelete(Variant::Map& values) { // sessions may be closed by a "queue deleted" exception. string name = values[QNAME].asString(); boost::shared_ptr<Queue> queue = broker.getQueues().find(name); - if (queue && replicateLevel(queue->getSettings())) { - QPID_LOG(debug, "HA: Backup deleting queue: " << name); + if (!queue) { + QPID_LOG(warning, "HA: Backup queue delete event, does not exist: " << name); + } else if (!replicateLevel(queue->getSettings())) { + QPID_LOG(warning, "HA: Backup queue delete event, not replicated: " << name); + } else { string rname = QueueReplicator::replicatorName(name); boost::shared_ptr<broker::Exchange> ex = broker.getExchanges().find(rname); boost::shared_ptr<QueueReplicator> qr = boost::dynamic_pointer_cast<QueueReplicator>(ex); @@ -310,6 +321,7 @@ void BrokerReplicator::doEventQueueDelete(Variant::Map& values) { // actually be destroyed, deleting the exhange broker.getExchanges().destroy(rname); broker.deleteQueue(name, values[USER].asString(), values[RHOST].asString()); + QPID_LOG(debug, "HA: Backup queue delete event: " << name); } } @@ -328,27 +340,29 @@ void BrokerReplicator::doEventExchangeDeclare(Variant::Map& values) { values[USER].asString(), values[RHOST].asString()).second) { - QPID_LOG(debug, "HA: Backup created exchange: " << name); + QPID_LOG(debug, "HA: Backup exchange declare event: " << name); } else { - // FIXME aconway 2011-11-22: should delete pre-exisitng exchange + // FIXME aconway 2011-11-22: should delete pre-existing exchange // and re-create from event. See comment in doEventQueueDeclare. - QPID_LOG(warning, "HA: Backup exchange already exists: " << name); + QPID_LOG(debug, "HA: Backup exchange declare event, already exists: " << name); } } } void BrokerReplicator::doEventExchangeDelete(Variant::Map& values) { string name = values[EXNAME].asString(); - try { - boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name); - if (exchange && replicateLevel(exchange->getArgs())) { - QPID_LOG(debug, "HA: Backup deleting exchange:" << name); - broker.deleteExchange( - name, - values[USER].asString(), - values[RHOST].asString()); - } - } catch (const framing::NotFoundException&) {} + boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name); + if (!exchange) { + QPID_LOG(warning, "HA: Backup exchange delete event, does not exist: " << name); + } else if (!replicateLevel(exchange->getArgs())) { + QPID_LOG(warning, "HA: Backup exchange delete event, not replicated: " << name); + } else { + QPID_LOG(debug, "HA: Backup exchange delete event:" << name); + broker.deleteExchange( + name, + values[USER].asString(), + values[RHOST].asString()); + } } void BrokerReplicator::doEventBind(Variant::Map& values) { @@ -364,10 +378,10 @@ void BrokerReplicator::doEventBind(Variant::Map& values) { framing::FieldTable args; amqp_0_10::translate(asMapVoid(values[ARGS]), args); string key = values[KEY].asString(); - QPID_LOG(debug, "HA: Backup replicated binding exchange=" << exchange->getName() + exchange->bind(queue, key, &args); + QPID_LOG(debug, "HA: Backup bind event: exchange=" << exchange->getName() << " queue=" << queue->getName() << " key=" << key); - exchange->bind(queue, key, &args); } } @@ -384,15 +398,14 @@ void BrokerReplicator::doEventUnbind(Variant::Map& values) { framing::FieldTable args; amqp_0_10::translate(asMapVoid(values[ARGS]), args); string key = values[KEY].asString(); - QPID_LOG(debug, "HA: Backup replicated unbinding exchange=" << exchange->getName() + exchange->unbind(queue, key, &args); + QPID_LOG(debug, "HA: Backup unbind event: exchange=" << exchange->getName() << " queue=" << queue->getName() << " key=" << key); - exchange->unbind(queue, key, &args); } } void BrokerReplicator::doResponseQueue(Variant::Map& values) { - // FIXME aconway 2011-11-22: more flexible ways & defaults to indicate replication Variant::Map argsMap(asMapVoid(values[ARGUMENTS])); if (!replicateLevel(argsMap)) return; framing::FieldTable args; @@ -409,12 +422,12 @@ void BrokerReplicator::doResponseQueue(Variant::Map& values) { ""/*TODO: who is the user?*/, ""/*TODO: what should we use as connection id?*/); if (result.second) { - QPID_LOG(debug, "HA: Backup created catch-up queue: " << values[NAME]); + QPID_LOG(debug, "HA: Backup queue response: " << name); startQueueReplicator(result.first); } else { // FIXME aconway 2011-11-22: Normal to find queue already // exists if we're failing over. - QPID_LOG(warning, "HA: Backup catch-up queue already exists: " << name); + QPID_LOG(warning, "HA: Backup queue response, already exists: " << name); } } @@ -432,9 +445,10 @@ void BrokerReplicator::doResponseExchange(Variant::Map& values) { ""/*TODO: who is the user?*/, ""/*TODO: what should we use as connection id?*/).second) { - QPID_LOG(debug, "HA: Backup catch-up exchange: " << values[NAME]); + QPID_LOG(debug, "HA: Backup exchange response: " << values[NAME].asString()); } else { - QPID_LOG(warning, "HA: Backup catch-up exchange already exists: " << values[QNAME]); + QPID_LOG(warning, "HA: Backup exchange query, already exists: " << + values[QNAME].asString()); } } @@ -464,7 +478,6 @@ void BrokerReplicator::doResponseBind(Variant::Map& values) { std::string qName = getRefName(QUEUE_REF_PREFIX, values[QUEUE_REF]); boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(exName); boost::shared_ptr<Queue> queue = broker.getQueues().find(qName); - // FIXME aconway 2011-11-24: more flexible configuration for binding replication. // Automatically replicate binding if queue and exchange exist and are replicated if (exchange && replicateLevel(exchange->getArgs()) && @@ -474,16 +487,39 @@ void BrokerReplicator::doResponseBind(Variant::Map& values) { amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); string key = values[KEY].asString(); exchange->bind(queue, key, &args); - QPID_LOG(debug, "HA: Backup catch-up binding: exchange=" << exchange->getName() + QPID_LOG(debug, "HA: Backup bind response: exchange=" << exchange->getName() << " queue=" << queue->getName() << " key=" << key); } } +namespace { +const string REPLICATE_DEFAULT="replicateDefault"; +} + +// Received the ha-broker configuration object for the primary broker. +void BrokerReplicator::doResponseHaBroker(Variant::Map& values) { + try { + ReplicateLevel mine = haBroker.getSettings().replicateDefault; + ReplicateLevel primary = replicateLevel(values[REPLICATE_DEFAULT].asString()); + if (mine != primary) { + std::ostringstream os; + os << "Replicate default on backup (" << mine + << ") does not match primary (" << primary << ")"; + haBroker.shutdown(os.str()); + } + } catch (const std::exception& e) { + std::ostringstream os; + os << "Received invalid replicate default from primary: " << e.what(); + haBroker.shutdown(os.str()); + } +} + void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue) { - if (replicateLevel(queue->getSettings()) == RL_MESSAGES) { + if (replicateLevel(queue->getSettings()) == RL_ALL) { boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link)); - broker.getExchanges().registerExchange(qr); + if (!broker.getExchanges().registerExchange(qr)) + throw Exception(QPID_MSG("Duplicate queue replicator " << qr->getName())); qr->activate(); } } diff --git a/cpp/src/qpid/ha/BrokerReplicator.h b/cpp/src/qpid/ha/BrokerReplicator.h index cfb6cf9a28..c9d7b9f74c 100644 --- a/cpp/src/qpid/ha/BrokerReplicator.h +++ b/cpp/src/qpid/ha/BrokerReplicator.h @@ -22,6 +22,7 @@ * */ +#include "ReplicateLevel.h" #include "qpid/broker/Exchange.h" #include "qpid/types/Variant.h" #include <boost/shared_ptr.hpp> @@ -35,7 +36,12 @@ class Bridge; class SessionHandler; } +namespace framing { +class FieldTable; +} + namespace ha { +class HaBroker; /** * Replicate configuration on a backup broker. @@ -51,19 +57,23 @@ namespace ha { class BrokerReplicator : public broker::Exchange { public: - BrokerReplicator(const boost::shared_ptr<broker::Link>&); + BrokerReplicator(HaBroker&, const boost::shared_ptr<broker::Link>&); ~BrokerReplicator(); std::string getType() const; // Exchange methods bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*); bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*); - void route(broker::Deliverable&, const std::string&, const framing::FieldTable*); + void route(broker::Deliverable&); bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const); private: void initializeBridge(broker::Bridge&, broker::SessionHandler&); + ReplicateLevel replicateLevel(const std::string&); + ReplicateLevel replicateLevel(const framing::FieldTable& args); + ReplicateLevel replicateLevel(const types::Variant::Map& args); + void doEventQueueDeclare(types::Variant::Map& values); void doEventQueueDelete(types::Variant::Map& values); void doEventExchangeDeclare(types::Variant::Map& values); @@ -74,9 +84,11 @@ class BrokerReplicator : public broker::Exchange void doResponseQueue(types::Variant::Map& values); void doResponseExchange(types::Variant::Map& values); void doResponseBind(types::Variant::Map& values); + void doResponseHaBroker(types::Variant::Map& values); void startQueueReplicator(const boost::shared_ptr<broker::Queue>&); + HaBroker& haBroker; broker::Broker& broker; boost::shared_ptr<broker::Link> link; }; diff --git a/cpp/src/qpid/ha/HaBroker.cpp b/cpp/src/qpid/ha/HaBroker.cpp index 0d3bd51439..7d82fb63bd 100644 --- a/cpp/src/qpid/ha/HaBroker.cpp +++ b/cpp/src/qpid/ha/HaBroker.cpp @@ -25,10 +25,15 @@ #include "ReplicatingSubscription.h" #include "qpid/Exception.h" #include "qpid/broker/Broker.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/SignalHandler.h" #include "qpid/management/ManagementAgent.h" #include "qmf/org/apache/qpid/ha/Package.h" -#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetClientAddresses.h" -#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokerAddresses.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerReplicate.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokers.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetPublicBrokers.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetExpectedBackups.h" #include "qpid/log/Statement.h" namespace qpid { @@ -40,8 +45,10 @@ using namespace std; namespace { -const std::string PRIMARY="primary"; +const std::string STANDALONE="standalone"; +const std::string CATCH_UP="catch-up"; const std::string BACKUP="backup"; +const std::string PRIMARY="primary"; } // namespace @@ -49,7 +56,6 @@ const std::string BACKUP="backup"; HaBroker::HaBroker(broker::Broker& b, const Settings& s) : broker(b), settings(s), - backup(new Backup(b, s)), mgmtObject(0) { // Register a factory for replicating subscriptions. @@ -62,15 +68,20 @@ HaBroker::HaBroker(broker::Broker& b, const Settings& s) ManagementAgent* ma = broker.getManagementAgent(); if (!ma) throw Exception("Cannot start HA: management is disabled"); - if (ma) { - _qmf::Package packageInit(ma); - mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker"); - mgmtObject->set_status(BACKUP); - ma->addObject(mgmtObject); - } + _qmf::Package packageInit(ma); + mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker"); + mgmtObject->set_status(settings.cluster ? BACKUP : STANDALONE); + mgmtObject->set_replicateDefault(str(settings.replicateDefault)); + ma->addObject(mgmtObject); + + // NOTE: lock is not needed in a constructor but we created it just to pass + // to the set functions. sys::Mutex::ScopedLock l(lock); if (!settings.clientUrl.empty()) setClientUrl(Url(settings.clientUrl), l); if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl), l); + + // If we are in a cluster, we start in backup mode. + if (settings.cluster) backup.reset(new Backup(*this, s)); } HaBroker::~HaBroker() {} @@ -80,26 +91,47 @@ Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args, switch (methodId) { case _qmf::HaBroker::METHOD_PROMOTE: { if (backup.get()) { // I am a backup - // FIXME aconway 2012-01-26: create primary state before resetting backup - // as that allows client connections. + // NOTE: resetting backup allows client connections, so any + // primary state should be set up here before backup.reset() backup.reset(); - QPID_LOG(notice, "HA: Primary promoted from backup"); + QPID_LOG(notice, "HA: Promoted to primary"); mgmtObject->set_status(PRIMARY); } break; } - case _qmf::HaBroker::METHOD_SETCLIENTADDRESSES: { - setClientUrl( - Url(dynamic_cast<_qmf::ArgsHaBrokerSetClientAddresses&>(args). - i_clientAddresses), l); + case _qmf::HaBroker::METHOD_SETBROKERS: { + setBrokerUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokers&>(args).i_url), l); + break; + } + case _qmf::HaBroker::METHOD_SETPUBLICBROKERS: { + setClientUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetPublicBrokers&>(args).i_url), l); break; } - case _qmf::HaBroker::METHOD_SETBROKERADDRESSES: { - setBrokerUrl( - Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokerAddresses&>(args) - .i_brokerAddresses), l); + case _qmf::HaBroker::METHOD_SETEXPECTEDBACKUPS: { + setExpectedBackups(dynamic_cast<_qmf::ArgsHaBrokerSetExpectedBackups&>(args).i_expectedBackups, l); + break; + } + case _qmf::HaBroker::METHOD_REPLICATE: { + _qmf::ArgsHaBrokerReplicate& bq_args = + dynamic_cast<_qmf::ArgsHaBrokerReplicate&>(args); + QPID_LOG(debug, "HA replicating individual queue "<< bq_args.i_queue << " from " << bq_args.i_broker); + + boost::shared_ptr<broker::Queue> queue = broker.getQueues().get(bq_args.i_queue); + Url url(bq_args.i_broker); + string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol; + std::pair<broker::Link::shared_ptr, bool> result = broker.getLinks().declare( + url[0].host, url[0].port, protocol, + false, // durable + settings.mechanism, settings.username, settings.password); + boost::shared_ptr<broker::Link> link = result.first; + link->setUrl(url); + // Create a queue replicator + boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link)); + qr->activate(); + broker.getExchanges().registerExchange(qr); break; } + default: return Manageable::STATUS_UNKNOWN_METHOD; } @@ -114,24 +146,35 @@ void HaBroker::setClientUrl(const Url& url, const sys::Mutex::ScopedLock& l) { void HaBroker::updateClientUrl(const sys::Mutex::ScopedLock&) { Url url = clientUrl.empty() ? brokerUrl : clientUrl; - assert(!url.empty()); - mgmtObject->set_clientAddresses(url.str()); + if (url.empty()) throw Url::Invalid("HA client URL is empty"); + mgmtObject->set_publicBrokers(url.str()); knownBrokers.clear(); knownBrokers.push_back(url); - QPID_LOG(debug, "HA: Setting client known-brokers to: " << url); + QPID_LOG(debug, "HA: Setting client URL to: " << url); } void HaBroker::setBrokerUrl(const Url& url, const sys::Mutex::ScopedLock& l) { - if (url.empty()) throw Exception("Invalid empty URL for HA broker failover"); + if (url.empty()) throw Url::Invalid("HA broker URL is empty"); + QPID_LOG(debug, "HA: Setting broker URL to: " << url); brokerUrl = url; - mgmtObject->set_brokerAddresses(brokerUrl.str()); + mgmtObject->set_brokers(brokerUrl.str()); if (backup.get()) backup->setBrokerUrl(brokerUrl); // Updating broker URL also updates defaulted client URL: if (clientUrl.empty()) updateClientUrl(l); } +void HaBroker::setExpectedBackups(size_t n, const sys::Mutex::ScopedLock&) { + expectedBackups = n; + mgmtObject->set_expectedBackups(n); +} + std::vector<Url> HaBroker::getKnownBrokers() const { return knownBrokers; } +void HaBroker::shutdown(const std::string& message) { + QPID_LOG(critical, "Shutting down: " << message); + broker.shutdown(); +} + }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/HaBroker.h b/cpp/src/qpid/ha/HaBroker.h index 835a47c749..99b30fd36b 100644 --- a/cpp/src/qpid/ha/HaBroker.h +++ b/cpp/src/qpid/ha/HaBroker.h @@ -52,9 +52,16 @@ class HaBroker : public management::Manageable management::Manageable::status_t ManagementMethod ( uint32_t methodId, management::Args& args, std::string& text); + broker::Broker& getBroker() { return broker; } + const Settings& getSettings() const { return settings; } + + // Log a critical error message and shut down the broker. + void shutdown(const std::string& message); + private: void setClientUrl(const Url&, const sys::Mutex::ScopedLock&); void setBrokerUrl(const Url&, const sys::Mutex::ScopedLock&); + void setExpectedBackups(size_t, const sys::Mutex::ScopedLock&); void updateClientUrl(const sys::Mutex::ScopedLock&); bool isPrimary(const sys::Mutex::ScopedLock&) { return !backup.get(); } std::vector<Url> getKnownBrokers() const; @@ -67,6 +74,7 @@ class HaBroker : public management::Manageable qmf::org::apache::qpid::ha::HaBroker* mgmtObject; Url clientUrl, brokerUrl; std::vector<Url> knownBrokers; + size_t expectedBackups; }; }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/HaPlugin.cpp b/cpp/src/qpid/ha/HaPlugin.cpp index fc9e48411d..4da3b0d7d2 100644 --- a/cpp/src/qpid/ha/HaPlugin.cpp +++ b/cpp/src/qpid/ha/HaPlugin.cpp @@ -31,12 +31,23 @@ struct Options : public qpid::Options { Settings& settings; Options(Settings& s) : qpid::Options("HA Options"), settings(s) { addOptions() - ("ha-enable", optValue(settings.enabled, "yes|no"), "Enable High Availability features") - ("ha-client-url", optValue(settings.clientUrl,"URL"), "URL that clients use to connect and fail over.") - ("ha-broker-url", optValue(settings.brokerUrl,"URL"), "URL that backup brokers use to connect and fail over.") - ("ha-username", optValue(settings.username, "USER"), "Username for connections between brokers") - ("ha-password", optValue(settings.password, "PASS"), "Password for connections between brokers") - ("ha-mechanism", optValue(settings.mechanism, "MECH"), "Authentication mechanism for connections between brokers") + ("ha-cluster", optValue(settings.cluster, "yes|no"), + "Join a HA active/passive cluster.") + ("ha-brokers", optValue(settings.brokerUrl,"URL"), + "URL that backup brokers use to connect and fail over.") + ("ha-public-brokers", optValue(settings.clientUrl,"URL"), + "URL that clients use to connect and fail over, defaults to ha-brokers.") + ("ha-replicate", + optValue(settings.replicateDefault, "LEVEL"), + "Replication level for creating queues and exchanges if there is no qpid.replicate argument supplied. LEVEL is 'none', 'configuration' or 'all'") + ("ha-expected-backups", optValue(settings.expectedBackups, "N"), + "Number of backups expected to be active in the HA cluster.") + ("ha-username", optValue(settings.username, "USER"), + "Username for connections between HA brokers") + ("ha-password", optValue(settings.password, "PASS"), + "Password for connections between HA brokers") + ("ha-mechanism", optValue(settings.mechanism, "MECH"), + "Authentication mechanism for connections between HA brokers") ; } }; @@ -55,10 +66,7 @@ struct HaPlugin : public Plugin { void initialize(Plugin::Target& target) { broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); - if (broker && settings.enabled) { - haBroker.reset(new ha::HaBroker(*broker, settings)); - } else - QPID_LOG(notice, "HA: Disabled"); + if (broker) haBroker.reset(new ha::HaBroker(*broker, settings)); } }; diff --git a/cpp/src/qpid/ha/QueueReplicator.cpp b/cpp/src/qpid/ha/QueueReplicator.cpp index 0017cc82cd..633619be13 100644 --- a/cpp/src/qpid/ha/QueueReplicator.cpp +++ b/cpp/src/qpid/ha/QueueReplicator.cpp @@ -30,8 +30,8 @@ #include "qpid/framing/SequenceSet.h" #include "qpid/framing/FieldTable.h" #include "qpid/log/Statement.h" +#include "qpid/Msg.h" #include <boost/shared_ptr.hpp> -#include <sstream> namespace { const std::string QPID_REPLICATOR_("qpid.replicator-"); @@ -54,10 +54,8 @@ std::string QueueReplicator::replicatorName(const std::string& queueName) { QueueReplicator::QueueReplicator(boost::shared_ptr<Queue> q, boost::shared_ptr<Link> l) : Exchange(replicatorName(q->getName()), 0, q->getBroker()), queue(q), link(l) { - std::stringstream ss; - ss << "HA: Backup " << queue->getName() << ": "; - logPrefix = ss.str(); - QPID_LOG(info, logPrefix << "Created, settings: " << q->getSettings()); + logPrefix = "HA: Backup of " + queue->getName() + ": "; + QPID_LOG(info, logPrefix << "Created"); } // This must be separate from the constructor so we can call shared_from_this. @@ -77,7 +75,7 @@ void QueueReplicator::activate() { 0, // sync? // Include shared_ptr to self to ensure we are not deleted // before initializeBridge is called. - boost::bind(&QueueReplicator::initializeBridge, this, _1, _2, shared_from_this()) + boost::bind(&QueueReplicator::initializeBridge, shared_from_this(), _1, _2) ); } @@ -91,9 +89,7 @@ void QueueReplicator::deactivate() { } // Called in a broker connection thread when the bridge is created. -// shared_ptr to self ensures we are not deleted before initializeBridge is called. -void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler, - boost::shared_ptr<QueueReplicator> /*self*/) { +void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler) { sys::Mutex::ScopedLock l(lock); bridgeName = bridge.getName(); framing::AMQP_ServerProxy peer(sessionHandler.out); @@ -141,27 +137,35 @@ void QueueReplicator::dequeue(SequenceNumber n, const sys::Mutex::ScopedLock&) } // Called in connection thread of the queues bridge to primary. -void QueueReplicator::route(Deliverable& msg, const std::string& key, const FieldTable*) +void QueueReplicator::route(Deliverable& msg) { - sys::Mutex::ScopedLock l(lock); - if (key == DEQUEUE_EVENT_KEY) { - SequenceSet dequeues = decodeContent<SequenceSet>(msg.getMessage()); - QPID_LOG(trace, logPrefix << "Dequeue: " << dequeues); - //TODO: should be able to optimise the following - for (SequenceSet::iterator i = dequeues.begin(); i != dequeues.end(); i++) - dequeue(*i, l); - } else if (key == POSITION_EVENT_KEY) { - SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage()); - QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition() - << " to " << position); - assert(queue->getPosition() <= position); - //TODO aconway 2011-12-14: Optimize this? - for (SequenceNumber i = queue->getPosition(); i < position; ++i) - dequeue(i,l); - queue->setPosition(position); - } else { - msg.deliverTo(queue); - QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition()); + try { + const std::string& key = msg.getMessage().getRoutingKey(); + sys::Mutex::ScopedLock l(lock); + if (key == DEQUEUE_EVENT_KEY) { + SequenceSet dequeues = decodeContent<SequenceSet>(msg.getMessage()); + QPID_LOG(trace, logPrefix << "Dequeue: " << dequeues); + //TODO: should be able to optimise the following + for (SequenceSet::iterator i = dequeues.begin(); i != dequeues.end(); i++) + dequeue(*i, l); + } else if (key == POSITION_EVENT_KEY) { + SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage()); + QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition() + << " to " << position); + if (queue->getPosition() > position) { + throw Exception( + QPID_MSG(logPrefix << "Invalid position update from " + << queue->getPosition() << " to " << position)); + } + queue->setPosition(position); + } else { + msg.deliverTo(queue); + QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition()); + } + } + catch (const std::exception& e) { + QPID_LOG(critical, logPrefix << "Replication failed: " << e.what()); + throw; } } diff --git a/cpp/src/qpid/ha/QueueReplicator.h b/cpp/src/qpid/ha/QueueReplicator.h index 9de7dd480c..bcbac988fa 100644 --- a/cpp/src/qpid/ha/QueueReplicator.h +++ b/cpp/src/qpid/ha/QueueReplicator.h @@ -66,12 +66,11 @@ class QueueReplicator : public broker::Exchange, bool bind(boost::shared_ptr<broker::Queue >, const std::string&, const framing::FieldTable*); bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*); - void route(broker::Deliverable&, const std::string&, const framing::FieldTable*); + void route(broker::Deliverable&); bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const); private: - void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler, - boost::shared_ptr<QueueReplicator> self); + void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler); void dequeue(framing::SequenceNumber, const sys::Mutex::ScopedLock&); std::string logPrefix; diff --git a/cpp/src/qpid/ha/ReplicateLevel.cpp b/cpp/src/qpid/ha/ReplicateLevel.cpp new file mode 100644 index 0000000000..4981577225 --- /dev/null +++ b/cpp/src/qpid/ha/ReplicateLevel.cpp @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ReplicateLevel.h" +#include "qpid/Exception.h" +#include "qpid/Msg.h" +#include <iostream> +#include <assert.h> + +namespace qpid { +namespace ha { + +using namespace std; + +// Note replicateLevel is called during plugin-initialization which +// happens in the static construction phase so these constants need +// to be POD, they can't be class objects +// +namespace { +const char* S_NONE="none"; +const char* S_CONFIGURATION="configuration"; +const char* S_ALL="all"; +} + +bool replicateLevel(const string& level, ReplicateLevel& out) { + if (level == S_NONE) { out = RL_NONE; return true; } + if (level == S_CONFIGURATION) { out = RL_CONFIGURATION; return true; } + if (level == S_ALL) { out = RL_ALL; return true; } + return false; +} + +ReplicateLevel replicateLevel(const string& level) { + ReplicateLevel rl; + if (!replicateLevel(level, rl)) + throw Exception("Invalid value for replication level: "+level); + return rl; +} + +string str(ReplicateLevel l) { + const char* names[] = { S_NONE, S_CONFIGURATION, S_ALL }; + if (l > RL_ALL) + throw Exception(QPID_MSG("Invalid value for replication level: " << l)); + return names[l]; +} + +ostream& operator<<(ostream& o, ReplicateLevel rl) { return o << str(rl); } + +istream& operator>>(istream& i, ReplicateLevel& rl) { + string str; + i >> str; + rl = replicateLevel(str); + return i; +} + +}} // namespace qpid::ha diff --git a/cpp/src/qpid/sys/apr/APRPool.h b/cpp/src/qpid/ha/ReplicateLevel.h index da7661fcfa..c11e03f0ce 100644 --- a/cpp/src/qpid/sys/apr/APRPool.h +++ b/cpp/src/qpid/ha/ReplicateLevel.h @@ -1,5 +1,5 @@ -#ifndef _APRPool_ -#define _APRPool_ +#ifndef QPID_HA_REPLICATELEVEL_H +#define QPID_HA_REPLICATELEVEL_H /* * @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -21,30 +21,32 @@ * under the License. * */ -#include <boost/noncopyable.hpp> -#include <apr_pools.h> -namespace qpid { -namespace sys { -/** - * Singleton APR memory pool. - */ -class APRPool : private boost::noncopyable { - public: - APRPool(); - ~APRPool(); +#include <string> +#include <iosfwd> - /** Get singleton instance */ - static apr_pool_t* get(); +namespace qpid { +namespace ha { - private: - apr_pool_t* pool; -}; +enum ReplicateLevel { RL_NONE, RL_CONFIGURATION, RL_ALL }; -}} +/** + * If str is a valid replicate level, set out and return true. + */ +bool replicateLevel(const std::string& str, ReplicateLevel& out); +/** + *@return enum corresponding to string level. + *@throw qpid::Exception if level is not a valid replication level. + */ +ReplicateLevel replicateLevel(const std::string& level); +/**@return string form of replicate level */ +std::string str(ReplicateLevel l); +std::ostream& operator<<(std::ostream&, ReplicateLevel); +std::istream& operator>>(std::istream&, ReplicateLevel&); +}} // namespaces qpid::ha -#endif /*!_APRPool_*/ +#endif /*!QPID_HA_REPLICATELEVEL_H*/ diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.cpp b/cpp/src/qpid/ha/ReplicatingSubscription.cpp index af6180305d..91a4538bc4 100644 --- a/cpp/src/qpid/ha/ReplicatingSubscription.cpp +++ b/cpp/src/qpid/ha/ReplicatingSubscription.cpp @@ -87,10 +87,13 @@ ReplicatingSubscription::ReplicatingSubscription( events(new Queue(mask(name))), consumer(new DelegatingConsumer(*this)) { + // Separate the remote part from a "local-remote" address. + string address = parent->getSession().getConnection().getUrl(); + size_t i = address.find('-'); + if (i != string::npos) address = address.substr(i+1); + logPrefix = "HA: Primary "; stringstream ss; - ss << "HA: Primary: " << getQueue()->getName() << " at " - << parent->getSession().getConnection().getUrl() << ": "; - logPrefix = ss.str(); + logSuffix = " (" + address + ")"; // FIXME aconway 2011-12-09: Failover optimization removed. // There was code here to re-use messages already on the backup @@ -99,7 +102,7 @@ ReplicatingSubscription::ReplicatingSubscription( // can be re-introduced later. Last revision with the optimization: // r1213258 | QPID-3603: Fix QueueReplicator subscription parameters. - QPID_LOG(debug, logPrefix << "Created backup subscription " << getName()); + QPID_LOG(debug, logPrefix << "created backup subscription " << getName() << logSuffix); // FIXME aconway 2011-12-15: ConsumerImpl::position is left at 0 // so we will start consuming from the lowest numbered message. @@ -109,23 +112,36 @@ ReplicatingSubscription::ReplicatingSubscription( // Message is delivered in the subscription's connection thread. bool ReplicatingSubscription::deliver(QueuedMessage& m) { - // Add position events for the subscribed queue, not for the internal event queue. - if (m.queue && m.queue == getQueue().get()) { - sys::Mutex::ScopedLock l(lock); - assert(position == m.position); - // m.position is the position of the newly enqueued m on the local queue. - // backupPosition is latest position on the backup queue (before enqueueing m.) - assert(m.position > backupPosition); - if (m.position - backupPosition > 1) { - // Position has advanced because of messages dequeued ahead of us. - SequenceNumber send(m.position); - --send; // Send the position before m was enqueued. - sendPositionEvent(send, l); + try { + // Add position events for the subscribed queue, not for the internal event queue. + if (m.queue && m.queue == getQueue().get()) { + sys::Mutex::ScopedLock l(lock); + if (position != m.position) + throw Exception( + QPID_MSG("Expected position " << position + << " but got " << m.position)); + // m.position is the position of the newly enqueued m on the local queue. + // backupPosition is latest position on the backup queue (before enqueueing m.) + if (m.position <= backupPosition) + throw Exception( + QPID_MSG("Expected position > " << backupPosition + << " but got " << m.position)); + + if (m.position - backupPosition > 1) { + // Position has advanced because of messages dequeued ahead of us. + SequenceNumber send(m.position); + --send; // Send the position before m was enqueued. + sendPositionEvent(send, l); + } + backupPosition = m.position; + QPID_LOG(trace, logPrefix << "replicating " << m << logSuffix); } - backupPosition = m.position; - QPID_LOG(trace, logPrefix << "Replicating message " << m.position); + return ConsumerImpl::deliver(m); + } catch (const std::exception& e) { + QPID_LOG(critical, logPrefix << "error replicating " << getQueue()->getName() + << logSuffix << ": " << e.what()); + throw; } - return ConsumerImpl::deliver(m); } ReplicatingSubscription::~ReplicatingSubscription() {} @@ -139,7 +155,7 @@ void ReplicatingSubscription::complete( { // Handle completions for the subscribed queue, not the internal event queue. if (qm.queue && qm.queue == getQueue().get()) { - QPID_LOG(trace, logPrefix << "Completed message " << qm.position); + QPID_LOG(trace, logPrefix << "completed " << qm << logSuffix); Delayed::iterator i= delayed.find(qm.position); // The same message can be completed twice, by acknowledged and // dequeued, remove it from the set so it only gets completed @@ -157,7 +173,7 @@ void ReplicatingSubscription::complete( void ReplicatingSubscription::enqueued(const QueuedMessage& qm) { sys::Mutex::ScopedLock l(lock); // Delay completion - QPID_LOG(trace, logPrefix << "Delaying completion of message " << qm.position); + QPID_LOG(trace, logPrefix << "delaying completion of " << qm << logSuffix); qm.payload->getIngressCompletion().startCompleter(); assert(delayed.find(qm.position) == delayed.end()); delayed[qm.position] = qm; @@ -168,7 +184,7 @@ void ReplicatingSubscription::enqueued(const QueuedMessage& qm) { void ReplicatingSubscription::cancelComplete( const Delayed::value_type& v, const sys::Mutex::ScopedLock&) { - QPID_LOG(trace, logPrefix << "Cancel completed message " << v.second.position); + QPID_LOG(trace, logPrefix << "cancel completed " << v.second << logSuffix); v.second.payload->getIngressCompletion().finishCompleter(); } @@ -179,7 +195,7 @@ void ReplicatingSubscription::cancel() boost::dynamic_pointer_cast<QueueObserver>(shared_from_this())); { sys::Mutex::ScopedLock l(lock); - QPID_LOG(debug, logPrefix <<"Cancelled backup subscription " << getName()); + QPID_LOG(debug, logPrefix << "cancel backup subscription " << getName() << logSuffix); for_each(delayed.begin(), delayed.end(), boost::bind(&ReplicatingSubscription::cancelComplete, this, _1, boost::ref(l))); delayed.clear(); @@ -201,7 +217,8 @@ bool ReplicatingSubscription::hideDeletedError() { return true; } // Called with lock held. Called in subscription's connection thread. void ReplicatingSubscription::sendDequeueEvent(const sys::Mutex::ScopedLock& l) { - QPID_LOG(trace, logPrefix << "Sending dequeues " << dequeues); + QPID_LOG(trace, logPrefix << "sending dequeues " << dequeues + << " from " << getQueue()->getName() << logSuffix); string buf(dequeues.encodedSize(),'\0'); framing::Buffer buffer(&buf[0], buf.size()); dequeues.encode(buffer); @@ -216,7 +233,7 @@ void ReplicatingSubscription::dequeued(const QueuedMessage& qm) { { sys::Mutex::ScopedLock l(lock); - QPID_LOG(trace, logPrefix << "Dequeued message " << qm.position); + QPID_LOG(trace, logPrefix << "dequeued " << qm << logSuffix); dequeues.add(qm.position); // If we have not yet sent this message to the backup, then // complete it now as it will never be accepted. @@ -229,8 +246,8 @@ void ReplicatingSubscription::dequeued(const QueuedMessage& qm) void ReplicatingSubscription::sendPositionEvent( SequenceNumber position, const sys::Mutex::ScopedLock&l ) { - QPID_LOG(trace, logPrefix << "Sending position " << position - << ", was " << backupPosition); + QPID_LOG(trace, logPrefix << "sending position " << position + << ", was " << backupPosition << logSuffix); string buf(backupPosition.encodedSize(),'\0'); framing::Buffer buffer(&buf[0], buf.size()); position.encode(buffer); diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.h b/cpp/src/qpid/ha/ReplicatingSubscription.h index e311f9505a..f9176915f6 100644 --- a/cpp/src/qpid/ha/ReplicatingSubscription.h +++ b/cpp/src/qpid/ha/ReplicatingSubscription.h @@ -33,7 +33,7 @@ namespace qpid { namespace broker { class Message; class Queue; -class QueuedMessage; +struct QueuedMessage; class OwnershipToken; } @@ -94,7 +94,7 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl, bool doDispatch(); private: typedef std::map<framing::SequenceNumber, broker::QueuedMessage> Delayed; - std::string logPrefix; + std::string logPrefix, logSuffix; boost::shared_ptr<broker::Queue> events; boost::shared_ptr<broker::Consumer> consumer; Delayed delayed; diff --git a/cpp/src/qpid/ha/Settings.h b/cpp/src/qpid/ha/Settings.h index 049c873b9f..bf70c3f3f7 100644 --- a/cpp/src/qpid/ha/Settings.h +++ b/cpp/src/qpid/ha/Settings.h @@ -22,6 +22,7 @@ * */ +#include "ReplicateLevel.h" #include <string> namespace qpid { @@ -33,10 +34,12 @@ namespace ha { class Settings { public: - Settings() : enabled(false) {} - bool enabled; + Settings() : cluster(false), expectedBackups(0), replicateDefault(RL_NONE) {} + bool cluster; // True if we are a cluster member. std::string clientUrl; std::string brokerUrl; + size_t expectedBackups; + ReplicateLevel replicateDefault; std::string username, password, mechanism; private: }; diff --git a/cpp/src/qpid/ha/management-schema.xml b/cpp/src/qpid/ha/management-schema.xml index fe4a14d111..363dff61fb 100644 --- a/cpp/src/qpid/ha/management-schema.xml +++ b/cpp/src/qpid/ha/management-schema.xml @@ -22,16 +22,39 @@ <!-- Monitor and control HA status of a broker. --> <class name="HaBroker"> <property name="name" type="sstr" access="RC" index="y" desc="Primary Key"/> + <property name="status" type="sstr" desc="HA status: primary or backup"/> - <property name="clientAddresses" type="sstr" desc="List of addresses used by clients to connect to the HA cluster."/> - <property name="brokerAddresses" type="sstr" desc="List of addresses used by HA brokers to connect to each other."/> + + <property name="brokers" type="sstr" + desc="Multiple-address URL used by HA brokers to connect to each other."/> + + <property name="publicBrokers" type="sstr" + desc="Multiple-address URL used by clients to connect to the HA brokers."/> + + <property name="expectedBackups" type="uint16" + desc="Number of HA backup brokers expected."/> + + <property + name="replicateDefault" type="sstr" + desc="Replicate value for queues/exchanges without a qpid.replicate argument"/> <method name="promote" desc="Promote a backup broker to primary."/> - <method name="setClientAddresses" desc="Set HA client addresses"> - <arg name="clientAddresses" type="sstr" dir="I"/> + + <method name="setBrokers" desc="Set URL for HA brokers to connect to each other."> + <arg name="url" type="sstr" dir="I"/> </method> - <method name="setBrokerAddresses" desc="Set HA broker addresses"> - <arg name="brokerAddresses" type="sstr" dir="I"/> + + <method name="setPublicBrokers" desc="Set URL for clients to connect to HA brokers"> + <arg name="url" type="sstr" dir="I"/> + </method> + + <method name="setExpectedBackups" desc="Set number of backups expected"> + <arg name="expectedBackups" type="uint16" dir="I"/> + </method> + + <method name="replicate" desc="Replicate individual queue from remote broker."> + <arg name="broker" type="sstr" dir="I"/> + <arg name="queue" type="sstr" dir="I"/> </method> </class> diff --git a/cpp/src/qpid/log/Statement.cpp b/cpp/src/qpid/log/Statement.cpp index 7dfdf08703..79f7a28100 100644 --- a/cpp/src/qpid/log/Statement.cpp +++ b/cpp/src/qpid/log/Statement.cpp @@ -55,6 +55,50 @@ void Statement::log(const std::string& message) { } Statement::Initializer::Initializer(Statement& s) : statement(s) { + // QPID-3891 + // From the given BOOST_CURRENT_FUNCTION name extract only the + // namespace-qualified-functionName, skipping return and calling args. + // Given: + // <possible return value type> qpid::name::space::Function(args) + // Return: + // "qpid::name::space::Function". + if (s.function != NULL) { + bool foundOParen(false); + const char * opPtr; + for (opPtr = s.function; *opPtr != '\0'; opPtr++) { + if (*opPtr == '(') { + foundOParen = true; + break; + } + } + + if (foundOParen) { + const char * bPtr = opPtr; + for (bPtr = opPtr; bPtr > s.function; bPtr--) { + if (bPtr[-1] == ' ') { + break; + } + } + + size_t nStoreSize = opPtr - bPtr; + if (nStoreSize > 0) { + // Note: the struct into which this name is stored + // is static and is never deleted. + char * nStore = new char[nStoreSize + 1]; + std::copy (bPtr, opPtr, nStore); + nStore[nStoreSize] = '\0'; + + s.function = nStore; + } else { + // Ignore zero length name + } + } else { + // No name found - do nothing + } + } else { + // no function-name pointer to process + } + Logger::instance().add(s); } diff --git a/cpp/src/qpid/log/posix/SinkOptions.cpp b/cpp/src/qpid/log/posix/SinkOptions.cpp index ffa7633e3b..8459938e5c 100644 --- a/cpp/src/qpid/log/posix/SinkOptions.cpp +++ b/cpp/src/qpid/log/posix/SinkOptions.cpp @@ -30,6 +30,10 @@ using std::string; using qpid::Exception; +namespace qpid { +namespace log { +namespace posix { + namespace { // SyslogFacilities maps from syslog values to the text equivalents. @@ -110,10 +114,6 @@ std::string basename(const std::string path) { } // namespace -namespace qpid { -namespace log { -namespace posix { - std::ostream& operator<<(std::ostream& o, const SyslogFacility& f) { return o << SyslogFacilities().name(f.value); } diff --git a/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp index 741ef442b0..062a530706 100644 --- a/cpp/src/qpid/management/ManagementAgent.cpp +++ b/cpp/src/qpid/management/ManagementAgent.cpp @@ -30,6 +30,7 @@ #include "qpid/log/Statement.h" #include <qpid/broker/Message.h> #include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/FieldValue.h" #include "qpid/sys/Time.h" #include "qpid/sys/Thread.h" #include "qpid/broker/ConnectionState.h" @@ -44,14 +45,15 @@ #include <sstream> #include <typeinfo> +namespace qpid { +namespace management { + using boost::intrusive_ptr; using qpid::framing::Uuid; using qpid::types::Variant; using qpid::amqp_0_10::MapCodec; using qpid::amqp_0_10::ListCodec; -using qpid::sys::Mutex; using namespace qpid::framing; -using namespace qpid::management; using namespace qpid::broker; using namespace qpid; using namespace std; @@ -117,7 +119,7 @@ ManagementAgent::RemoteAgent::~RemoteAgent () } ManagementAgent::ManagementAgent (const bool qmfV1, const bool qmfV2) : - threadPoolSize(1), interval(10), broker(0), timer(0), + threadPoolSize(1), publish(true), interval(10), broker(0), timer(0), startTime(sys::now()), suppressed(false), disallowAllV1Methods(false), vendorNameKey(defaultVendorName), productNameKey(defaultProductName), @@ -164,10 +166,11 @@ ManagementAgent::~ManagementAgent () } } -void ManagementAgent::configure(const string& _dataDir, uint16_t _interval, +void ManagementAgent::configure(const string& _dataDir, bool _publish, uint16_t _interval, qpid::broker::Broker* _broker, int _threads) { dataDir = _dataDir; + publish = _publish; interval = _interval; broker = _broker; threadPoolSize = _threads; @@ -428,16 +431,17 @@ void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severi } ManagementAgent::Periodic::Periodic (ManagementAgent& _agent, uint32_t _seconds) - : TimerTask (sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC), - "ManagementAgent::periodicProcessing"), + : TimerTask(sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC), + "ManagementAgent::periodicProcessing"), agent(_agent) {} -ManagementAgent::Periodic::~Periodic () {} +ManagementAgent::Periodic::~Periodic() {} -void ManagementAgent::Periodic::fire () +void ManagementAgent::Periodic::fire() { - agent.timer->add (new Periodic (agent, agent.interval)); - agent.periodicProcessing (); + setupNextFire(); + agent.timer->add(this); + agent.periodicProcessing(); } void ManagementAgent::clientAdded (const string& routingKey) @@ -562,7 +566,7 @@ void ManagementAgent::sendBufferLH(Buffer& buf, DeliverableMessage deliverable (msg); try { - exchange->route(deliverable, routingKey, 0); + exchange->route(deliverable); } catch(exception&) {} } buf.reset(); @@ -639,7 +643,7 @@ void ManagementAgent::sendBufferLH(const string& data, DeliverableMessage deliverable (msg); try { - exchange->route(deliverable, routingKey, 0); + exchange->route(deliverable); } catch(exception&) {} } } @@ -719,11 +723,16 @@ void ManagementAgent::periodicProcessing (void) string routingKey; string sBuf; - uint64_t uptime = sys::Duration(startTime, sys::now()); - static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); - moveNewObjectsLH(); - qpid::sys::MemStat::loadMemInfo(memstat); + + // + // If we're publishing updates, get the latest memory statistics and uptime now + // + if (publish) { + uint64_t uptime = sys::Duration(startTime, sys::now()); + static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); + qpid::sys::MemStat::loadMemInfo(memstat); + } // // Clear the been-here flag on all objects in the map. @@ -747,6 +756,14 @@ void ManagementAgent::periodicProcessing (void) // would incorrectly think the object was deleted. See QPID-2997 // bool objectsDeleted = moveDeletedObjectsLH(); + + // + // If we are not publishing updates, just clear the pending deletes. There's no + // need to tell anybody. + // + if (!publish) + pendingDeletedObjs.clear(); + if (!pendingDeletedObjs.empty()) { // use a temporary copy of the pending deletes so dropping the lock when // the buffer is sent is safe. @@ -867,7 +884,9 @@ void ManagementAgent::periodicProcessing (void) // sendBuffer(). This allows the managementObjects map to be altered during the // sendBuffer() call, so always restart the search after a sendBuffer() call // - while (1) { + // If publish is disabled, don't send any updates. + // + while (publish) { msgBuffer.reset(); Variant::List list_; uint32_t pcount; @@ -1023,10 +1042,9 @@ void ManagementAgent::periodicProcessing (void) if (objectsDeleted) deleteOrphanedAgentsLH(); - // heartbeat generation + // heartbeat generation. Note that heartbeats need to be sent even if publish is disabled. if (qmf1Support) { -#define BUFSIZE 65536 uint32_t contentSize; char msgChars[BUFSIZE]; Buffer msgBuffer(msgChars, BUFSIZE); @@ -1087,7 +1105,7 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) Variant::List list_; stringstream v1key, v2key; - if (qmf1Support) { + if (publish && qmf1Support) { string sBuf; v1key << "console.obj.1.0." << object->getPackageName() << "." << object->getClassName(); @@ -1096,7 +1114,7 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) msgBuffer.putRawData(sBuf); } - if (qmf2Support) { + if (publish && qmf2Support) { Variant::Map map_; Variant::Map values; @@ -1121,14 +1139,14 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) // object deleted, ok to drop lock now. - if (qmf1Support) { + if (publish && qmf1Support) { uint32_t contentSize = msgBuffer.getPosition(); msgBuffer.reset(); sendBufferLH(msgBuffer, contentSize, mExchange, v1key.str()); QPID_LOG(debug, "SEND Immediate(delete) ContentInd to=" << v1key.str()); } - if (qmf2Support) { + if (publish && qmf2Support) { Variant::Map headers; headers["method"] = "indication"; headers["qmf.opcode"] = "_data_indication"; @@ -1841,6 +1859,12 @@ void ManagementAgent::handleGetQueryLH(Buffer& inBuffer, const string& replyToKe if (className == "memory") qpid::sys::MemStat::loadMemInfo(memstat); + if (className == "broker") { + uint64_t uptime = sys::Duration(startTime, sys::now()); + static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); + } + + // build up a set of all objects to be dumped for (ManagementObjectMap::iterator iter = managementObjects.begin(); iter != managementObjects.end(); @@ -1956,6 +1980,11 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co if (className == "memory") qpid::sys::MemStat::loadMemInfo(memstat); + if (className == "broker") { + uint64_t uptime = sys::Duration(startTime, sys::now()); + static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); + } + /* * Unpack the _object_id element of the query if it is present. If it is present, find that one * object and return it. If it is not present, send a class-based result. @@ -2934,9 +2963,6 @@ bool ManagementAgent::moveDeletedObjectsLH() { return !deleteList.empty(); } -namespace qpid { -namespace management { - namespace { QPID_TSS const qpid::broker::ConnectionState* executionContext = 0; } @@ -2951,3 +2977,4 @@ const qpid::broker::ConnectionState* getManagementExecutionContext() } }} + diff --git a/cpp/src/qpid/management/ManagementAgent.h b/cpp/src/qpid/management/ManagementAgent.h index f68bfe0577..c7e830dcf5 100644 --- a/cpp/src/qpid/management/ManagementAgent.h +++ b/cpp/src/qpid/management/ManagementAgent.h @@ -36,7 +36,6 @@ #include "qpid/sys/MemStat.h" #include "qpid/types/Variant.h" #include <qpid/framing/AMQFrame.h> -#include <qpid/framing/FieldValue.h> #include <qpid/framing/ResizableBuffer.h> #include <memory> #include <string> @@ -72,7 +71,7 @@ public: virtual ~ManagementAgent (); /** Called before plugins are initialized */ - void configure (const std::string& dataDir, uint16_t interval, + void configure (const std::string& dataDir, bool publish, uint16_t interval, qpid::broker::Broker* broker, int threadPoolSize); /** Called after plugins are initialized. */ void pluginsInitialized(); @@ -300,6 +299,7 @@ private: qpid::broker::Exchange::shared_ptr v2Topic; qpid::broker::Exchange::shared_ptr v2Direct; std::string dataDir; + bool publish; uint16_t interval; qpid::broker::Broker* broker; qpid::sys::Timer* timer; diff --git a/cpp/src/qpid/management/ManagementDirectExchange.cpp b/cpp/src/qpid/management/ManagementDirectExchange.cpp index 1d5f8bbd6b..312eacf462 100644 --- a/cpp/src/qpid/management/ManagementDirectExchange.cpp +++ b/cpp/src/qpid/management/ManagementDirectExchange.cpp @@ -40,17 +40,17 @@ ManagementDirectExchange::ManagementDirectExchange(const std::string& _name, DirectExchange(_name, _durable, _args, _parent, b), managementAgent(0) {} -void ManagementDirectExchange::route(Deliverable& msg, - const string& routingKey, - const FieldTable* args) +void ManagementDirectExchange::route(Deliverable& msg) { bool routeIt = true; + const string& routingKey = msg.getMessage().getRoutingKey(); + const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (managementAgent) routeIt = managementAgent->dispatchCommand(msg, routingKey, args, false, qmfVersion); if (routeIt) - DirectExchange::route(msg, routingKey, args); + DirectExchange::route(msg); } void ManagementDirectExchange::setManagmentAgent(ManagementAgent* agent, int qv) diff --git a/cpp/src/qpid/management/ManagementDirectExchange.h b/cpp/src/qpid/management/ManagementDirectExchange.h index 7507179c06..582354d723 100644 --- a/cpp/src/qpid/management/ManagementDirectExchange.h +++ b/cpp/src/qpid/management/ManagementDirectExchange.h @@ -43,9 +43,7 @@ class ManagementDirectExchange : public virtual DirectExchange virtual std::string getType() const { return typeName; } - virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg); void setManagmentAgent(management::ManagementAgent* agent, int qmfVersion); diff --git a/cpp/src/qpid/management/ManagementTopicExchange.cpp b/cpp/src/qpid/management/ManagementTopicExchange.cpp index ee8657646f..587cc660df 100644 --- a/cpp/src/qpid/management/ManagementTopicExchange.cpp +++ b/cpp/src/qpid/management/ManagementTopicExchange.cpp @@ -39,18 +39,18 @@ ManagementTopicExchange::ManagementTopicExchange(const std::string& _name, TopicExchange(_name, _durable, _args, _parent, b), managementAgent(0) {} -void ManagementTopicExchange::route(Deliverable& msg, - const string& routingKey, - const FieldTable* args) +void ManagementTopicExchange::route(Deliverable& msg) { bool routeIt = true; + const string& routingKey = msg.getMessage().getRoutingKey(); + const FieldTable* args = msg.getMessage().getApplicationHeaders(); // Intercept management agent commands if (managementAgent) routeIt = managementAgent->dispatchCommand(msg, routingKey, args, true, qmfVersion); if (routeIt) - TopicExchange::route(msg, routingKey, args); + TopicExchange::route(msg); } bool ManagementTopicExchange::bind(Queue::shared_ptr queue, diff --git a/cpp/src/qpid/management/ManagementTopicExchange.h b/cpp/src/qpid/management/ManagementTopicExchange.h index 232300265e..eff01a8552 100644 --- a/cpp/src/qpid/management/ManagementTopicExchange.h +++ b/cpp/src/qpid/management/ManagementTopicExchange.h @@ -43,9 +43,7 @@ class ManagementTopicExchange : public virtual TopicExchange virtual std::string getType() const { return typeName; } - virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg); virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, diff --git a/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/cpp/src/qpid/replication/ReplicatingEventListener.cpp index 0ced4d9161..9284bda388 100644 --- a/cpp/src/qpid/replication/ReplicatingEventListener.cpp +++ b/cpp/src/qpid/replication/ReplicatingEventListener.cpp @@ -80,7 +80,7 @@ void ReplicatingEventListener::route(boost::intrusive_ptr<qpid::broker::Message> try { if (exchange) { DeliverableMessage deliverable(msg); - exchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders()); + exchange->route(deliverable); } else if (queue) { queue->deliver(msg); } else { @@ -131,7 +131,7 @@ boost::intrusive_ptr<Message> ReplicatingEventListener::cloneMessage(Queue& queu //cloned body: AMQFrame header(*original->getFrames().getHeaders()); header.setBof(false); - header.setEof(!original->getFrames().getContentSize());//if there is any content then the header is not the end of the frameset + header.setEof(!original->getFrames().hasContent());//if there are any content frames then the header is not the end of the frameset header.setBos(true); header.setEos(true); handler.handle(header); diff --git a/cpp/src/qpid/replication/ReplicationExchange.cpp b/cpp/src/qpid/replication/ReplicationExchange.cpp index 89a2bf516d..66f4f14d0c 100644 --- a/cpp/src/qpid/replication/ReplicationExchange.cpp +++ b/cpp/src/qpid/replication/ReplicationExchange.cpp @@ -50,8 +50,9 @@ ReplicationExchange::ReplicationExchange(const std::string& name, bool durable, std::string ReplicationExchange::getType() const { return typeName; } -void ReplicationExchange::route(Deliverable& msg, const std::string& /*routingKey*/, const FieldTable* args) +void ReplicationExchange::route(Deliverable& msg) { + const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (mgmtExchange != 0) { mgmtExchange->inc_msgReceives(); mgmtExchange->inc_byteReceives(msg.contentSize()); @@ -59,7 +60,13 @@ void ReplicationExchange::route(Deliverable& msg, const std::string& /*routingKe if (args) { int eventType = args->getAsInt(REPLICATION_EVENT_TYPE); if (eventType) { - if (isDuplicate(args)) return; + if (isDuplicate(args)) { + if (mgmtExchange != 0) { + mgmtExchange->inc_msgDrops(); + mgmtExchange->inc_byteDrops(msg.contentSize()); + } + return; + } switch (eventType) { case ENQUEUE: handleEnqueueEvent(args, msg); diff --git a/cpp/src/qpid/replication/ReplicationExchange.h b/cpp/src/qpid/replication/ReplicationExchange.h index 4b34e0df13..ff0a98c48e 100644 --- a/cpp/src/qpid/replication/ReplicationExchange.h +++ b/cpp/src/qpid/replication/ReplicationExchange.h @@ -52,7 +52,7 @@ class ReplicationExchange : public qpid::broker::Exchange std::string getType() const; - void route(qpid::broker::Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + void route(qpid::broker::Deliverable& msg); bool bind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args); bool unbind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args); diff --git a/cpp/src/qpid/store/MessageStorePlugin.cpp b/cpp/src/qpid/store/MessageStorePlugin.cpp index 2a8d971987..20231bf910 100644 --- a/cpp/src/qpid/store/MessageStorePlugin.cpp +++ b/cpp/src/qpid/store/MessageStorePlugin.cpp @@ -28,6 +28,9 @@ #include "qpid/DataDir.h" #include "qpid/log/Statement.h" +namespace qpid { +namespace store { + /* * The MessageStore pointer given to the Broker points to static storage. * Thus, it cannot be deleted, especially by the broker. To prevent deletion, @@ -42,9 +45,6 @@ namespace { }; } -namespace qpid { -namespace store { - static MessageStorePlugin static_instance_registers_plugin; diff --git a/cpp/src/qpid/store/ms-clfs/MessageLog.cpp b/cpp/src/qpid/store/ms-clfs/MessageLog.cpp index 14d63a4cd4..849a0a44e8 100644 --- a/cpp/src/qpid/store/ms-clfs/MessageLog.cpp +++ b/cpp/src/qpid/store/ms-clfs/MessageLog.cpp @@ -32,6 +32,10 @@ #include "MessageLog.h" #include "Lsn.h" +namespace qpid { +namespace store { +namespace ms_clfs { + namespace { // Structures that hold log records. Each has a type field at the start. @@ -97,10 +101,6 @@ struct MessageDequeue { } // namespace -namespace qpid { -namespace store { -namespace ms_clfs { - void MessageLog::initialize() { diff --git a/cpp/src/qpid/store/ms-clfs/Transaction.h b/cpp/src/qpid/store/ms-clfs/Transaction.h index fd07f2fb2e..499b01d503 100644 --- a/cpp/src/qpid/store/ms-clfs/Transaction.h +++ b/cpp/src/qpid/store/ms-clfs/Transaction.h @@ -27,6 +27,7 @@ #include <boost/enable_shared_from_this.hpp> #include <boost/shared_ptr.hpp> #include <string> +#include <vector> #include "TransactionLog.h" diff --git a/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp b/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp index 04780e83e8..0ef046d7c8 100644 --- a/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp +++ b/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp @@ -33,6 +33,10 @@ #include "Transaction.h" #include "Lsn.h" +namespace qpid { +namespace store { +namespace ms_clfs { + namespace { // Structures that hold log records. Each has a type field at the start. @@ -95,10 +99,6 @@ struct TransactionDelete { } // namespace -namespace qpid { -namespace store { -namespace ms_clfs { - void TransactionLog::initialize() { diff --git a/cpp/include/qpid/sys/MemStat.h b/cpp/src/qpid/sys/MemStat.h index d855786cd5..d855786cd5 100644 --- a/cpp/include/qpid/sys/MemStat.h +++ b/cpp/src/qpid/sys/MemStat.h diff --git a/cpp/src/qpid/sys/Probes.h b/cpp/src/qpid/sys/Probes.h new file mode 100644 index 0000000000..d30181c357 --- /dev/null +++ b/cpp/src/qpid/sys/Probes.h @@ -0,0 +1,65 @@ +#ifndef _sys_Probes +#define _sys_Probes +/* + * + * 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 "config.h" + +#ifdef HAVE_SYS_SDT_H +#include <sys/sdt.h> +#endif + +// Pragmatically it seems that Linux and Solaris versions of sdt.h which support +// user static probes define up to DTRACE_PROBE8, but FreeBSD 8 which doesn't +// support usdt only defines up to DTRACE_PROBE4 - FreeBSD 9 which does support usdt +// defines up to DTRACE_PROBE5. + +#ifdef DTRACE_PROBE5 +// Versions for Linux Systemtap/Solaris/FreeBSD 9 +#define QPID_PROBE(probe) DTRACE_PROBE(qpid, probe) +#define QPID_PROBE1(probe, p1) DTRACE_PROBE1(qpid, probe, p1) +#define QPID_PROBE2(probe, p1, p2) DTRACE_PROBE2(qpid, probe, p1, p2) +#define QPID_PROBE3(probe, p1, p2, p3) DTRACE_PROBE3(qpid, probe, p1, p2, p3) +#define QPID_PROBE4(probe, p1, p2, p3, p4) DTRACE_PROBE4(qpid, probe, p1, p2, p3, p4) +#define QPID_PROBE5(probe, p1, p2, p3, p4, p5) DTRACE_PROBE5(qpid, probe, p1, p2, p3, p4, p5) +#else +// FreeBSD 8 +#define QPID_PROBE(probe) +#define QPID_PROBE1(probe, p1) +#define QPID_PROBE2(probe, p1, p2) +#define QPID_PROBE3(probe, p1, p2, p3) +#define QPID_PROBE4(probe, p1, p2, p3, p4) +#define QPID_PROBE5(probe, p1, p2, p3, p4, p5) +#endif + +#ifdef DTRACE_PROBE8 +// Versions for Linux Systemtap +#define QPID_PROBE6(probe, p1, p2, p3, p4, p5, p6) DTRACE_PROBE6(qpid, probe, p1, p2, p3, p4, p5, p6) +#define QPID_PROBE7(probe, p1, p2, p3, p4, p5, p6, p7) DTRACE_PROBE7(qpid, probe, p1, p2, p3, p4, p5, p6, p7) +#define QPID_PROBE8(probe, p1, p2, p3, p4, p5, p6, p7, p8) DTRACE_PROBE8(qpid, probe, p1, p2, p3, p4, p5, p6, p7, p8) +#else +// Versions for Solaris/FreeBSD +#define QPID_PROBE6(probe, p1, p2, p3, p4, p5, p6) +#define QPID_PROBE7(probe, p1, p2, p3, p4, p5, p6, p7) +#define QPID_PROBE8(probe, p1, p2, p3, p4, p5, p6, p7, p8) +#endif + +#endif // _sys_Probes diff --git a/cpp/src/qpid/sys/apr/APRBase.cpp b/cpp/src/qpid/sys/apr/APRBase.cpp deleted file mode 100644 index 8bdba66bdc..0000000000 --- a/cpp/src/qpid/sys/apr/APRBase.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include <iostream> -#include "qpid/log/Statement.h" -#include "qpid/sys/apr/APRBase.h" - -using namespace qpid::sys; - -APRBase* APRBase::instance = 0; - -APRBase* APRBase::getInstance(){ - if(instance == 0){ - instance = new APRBase(); - } - return instance; -} - - -APRBase::APRBase() : count(0){ - apr_initialize(); - CHECK_APR_SUCCESS(apr_pool_create(&pool, 0)); - CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); -} - -APRBase::~APRBase(){ - CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); - apr_pool_destroy(pool); - apr_terminate(); -} - -bool APRBase::_increment(){ - bool deleted(false); - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); - if(this == instance){ - count++; - }else{ - deleted = true; - } - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); - return !deleted; -} - -void APRBase::_decrement(){ - APRBase* copy = 0; - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); - if(--count == 0){ - copy = instance; - instance = 0; - } - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); - if(copy != 0){ - delete copy; - } -} - -void APRBase::increment(){ - int count = 0; - while(count++ < 2 && !getInstance()->_increment()) - QPID_LOG(warning, "APR initialization triggered concurrently with termination."); -} - -void APRBase::decrement(){ - getInstance()->_decrement(); -} - -std::string qpid::sys::get_desc(apr_status_t status){ - const int size = 50; - char tmp[size]; - return std::string(apr_strerror(status, tmp, size)); -} - diff --git a/cpp/src/qpid/sys/apr/APRBase.h b/cpp/src/qpid/sys/apr/APRBase.h deleted file mode 100644 index 7b5644a129..0000000000 --- a/cpp/src/qpid/sys/apr/APRBase.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _APRBase_ -#define _APRBase_ - -#include <string> -#include <apr_thread_mutex.h> -#include <apr_errno.h> - -namespace qpid { -namespace sys { - - /** - * Use of APR libraries necessitates explicit init and terminate - * calls. Any class using APR libs should obtain the reference to - * this singleton and increment on construction, decrement on - * destruction. This class can then correctly initialise apr - * before the first use and terminate after the last use. - */ - class APRBase{ - static APRBase* instance; - apr_pool_t* pool; - apr_thread_mutex_t* mutex; - int count; - - APRBase(); - ~APRBase(); - static APRBase* getInstance(); - bool _increment(); - void _decrement(); - public: - static void increment(); - static void decrement(); - }; - - //this is also a convenient place for a helper function for error checking: - void check(apr_status_t status, const char* file, const int line); - std::string get_desc(apr_status_t status); - -#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__); - -} -} - -// Inlined as it is called *a lot* -void inline qpid::sys::check(apr_status_t status, const char* file, const int line){ - if (status != APR_SUCCESS){ - char tmp[256]; - throw Exception(QPID_MSG(apr_strerror(status, tmp, size))) - } -} - - - - -#endif diff --git a/cpp/src/qpid/sys/apr/Condition.h b/cpp/src/qpid/sys/apr/Condition.h deleted file mode 100644 index 66d465ca75..0000000000 --- a/cpp/src/qpid/sys/apr/Condition.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef _sys_apr_Condition_h -#define _sys_apr_Condition_h - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/apr/APRPool.h" -#include "qpid/sys/Mutex.h" -#include "qpid/sys/Time.h" - -#include <sys/errno.h> -#include <boost/noncopyable.hpp> -#include <apr_thread_cond.h> - -namespace qpid { -namespace sys { - -/** - * A condition variable for thread synchronization. - */ -class Condition -{ - public: - inline Condition(); - inline ~Condition(); - inline void wait(Mutex&); - inline bool wait(Mutex&, const AbsTime& absoluteTime); - inline void notify(); - inline void notifyAll(); - - private: - apr_thread_cond_t* condition; -}; - - -Condition::Condition() { - CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get())); -} - -Condition::~Condition() { - CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); -} - -void Condition::wait(Mutex& mutex) { - CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex)); -} - -bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ - // APR uses microseconds. - apr_status_t status = - apr_thread_cond_timedwait( - condition, mutex.mutex, Duration(now(), absoluteTime)/TIME_USEC); - if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status); - return status == 0; -} - -void Condition::notify(){ - CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); -} - -void Condition::notifyAll(){ - CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); -} - -}} -#endif /*!_sys_apr_Condition_h*/ diff --git a/cpp/src/qpid/sys/apr/Mutex.h b/cpp/src/qpid/sys/apr/Mutex.h deleted file mode 100644 index cb75f5b339..0000000000 --- a/cpp/src/qpid/sys/apr/Mutex.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef _sys_apr_Mutex_h -#define _sys_apr_Mutex_h - -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "qpid/sys/apr/APRBase.h" -#include "qpid/sys/apr/APRPool.h" - -#include <boost/noncopyable.hpp> -#include <apr_thread_mutex.h> - -namespace qpid { -namespace sys { - -class Condition; - -/** - * Mutex lock. - */ -class Mutex : private boost::noncopyable { - public: - typedef ScopedLock<Mutex> ScopedLock; - typedef ScopedUnlock<Mutex> ScopedUnlock; - - inline Mutex(); - inline ~Mutex(); - inline void lock(); - inline void unlock(); - inline bool trylock(); - - protected: - apr_thread_mutex_t* mutex; - friend class Condition; -}; - -Mutex::Mutex() { - CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); -} - -Mutex::~Mutex(){ - CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); -} - -void Mutex::lock() { - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); -} -void Mutex::unlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); -} - -bool Mutex::trylock() { - return apr_thread_mutex_trylock(mutex) == 0; -} - - -/** - * RW lock. - */ -class RWlock : private boost::noncopyable { - friend class Condition; - -public: - typedef ScopedRlock<RWlock> ScopedRlock; - typedef ScopedWlock<RWlock> ScopedWlock; - - inline RWlock(); - inline ~RWlock(); - inline void wlock(); // will write-lock - inline void rlock(); // will read-lock - inline void unlock(); - inline bool trywlock(); // will write-try - inline bool tryrlock(); // will read-try - - protected: - apr_thread_mutex_t* mutex; -}; - -RWlock::RWlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); -} - -RWlock::~RWlock(){ - CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); -} - -void RWlock::wlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); -} - -void RWlock::rlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); -} - -void RWlock::unlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); -} - -bool RWlock::trywlock() { - return apr_thread_mutex_trylock(mutex) == 0; -} - -bool RWlock::tryrlock() { - return apr_thread_mutex_trylock(mutex) == 0; -} - - -}} -#endif /*!_sys_apr_Mutex_h*/ diff --git a/cpp/src/qpid/sys/apr/Shlib.cpp b/cpp/src/qpid/sys/apr/Shlib.cpp deleted file mode 100644 index b7ee13a03b..0000000000 --- a/cpp/src/qpid/sys/apr/Shlib.cpp +++ /dev/null @@ -1,49 +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/sys/Shlib.h" -#include "qpid/sys/apr/APRBase.h" -#include "qpid/sys/apr/APRPool.h" -#include <apr_dso.h> - -namespace qpid { -namespace sys { - -void Shlib::load(const char* libname) { - apr_dso_handle_t* aprHandle; - CHECK_APR_SUCCESS( - apr_dso_load(&aprHandle, libname, APRPool::get())); - handle=aprHandle; -} - -void Shlib::unload() { - CHECK_APR_SUCCESS( - apr_dso_unload(static_cast<apr_dso_handle_t*>(handle))); -} - -void* Shlib::getSymbol(const char* name) { - apr_dso_handle_sym_t symbol; - CHECK_APR_SUCCESS(apr_dso_sym(&symbol, - static_cast<apr_dso_handle_t*>(handle), - name)); - return (void*) symbol; -} - -}} // namespace qpid::sys diff --git a/cpp/src/qpid/sys/apr/Socket.cpp b/cpp/src/qpid/sys/apr/Socket.cpp deleted file mode 100644 index d9024d11c1..0000000000 --- a/cpp/src/qpid/sys/apr/Socket.cpp +++ /dev/null @@ -1,114 +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/sys/Socket.h" - -#include "qpid/sys/apr/APRBase.h" -#include "qpid/sys/apr/APRPool.h" - -#include <apr_network_io.h> - -namespace qpid { -namespace sys { - -class SocketPrivate { -public: - SocketPrivate(apr_socket_t* s = 0) : - socket(s) - {} - - apr_socket_t* socket; -}; - -Socket::Socket() : - impl(new SocketPrivate) -{ - createTcp(); -} - -Socket::Socket(SocketPrivate* sp) : - impl(sp) -{} - -Socket::~Socket() { - delete impl; -} - -void Socket::createTcp() const { - apr_socket_t*& socket = impl->socket; - apr_socket_t* s; - CHECK_APR_SUCCESS( - apr_socket_create( - &s, APR_INET, SOCK_STREAM, APR_PROTO_TCP, - APRPool::get())); - socket = s; -} - -void Socket::setTimeout(const Duration& interval) const { - apr_socket_t*& socket = impl->socket; - apr_socket_timeout_set(socket, interval/TIME_USEC); -} - -void Socket::connect(const std::string& host, int port) const { - apr_socket_t*& socket = impl->socket; - apr_sockaddr_t* address; - CHECK_APR_SUCCESS( - apr_sockaddr_info_get( - &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, - APRPool::get())); - CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); -} - -void Socket::close() const { - apr_socket_t*& socket = impl->socket; - if (socket == 0) return; - CHECK_APR_SUCCESS(apr_socket_close(socket)); - socket = 0; -} - -ssize_t Socket::send(const void* data, size_t size) const -{ - apr_socket_t*& socket = impl->socket; - apr_size_t sent = size; - apr_status_t status = - apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent); - if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT; - if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF; - CHECK_APR_SUCCESS(status); - return sent; -} - -ssize_t Socket::recv(void* data, size_t size) const -{ - apr_socket_t*& socket = impl->socket; - apr_size_t received = size; - apr_status_t status = - apr_socket_recv(socket, reinterpret_cast<char*>(data), &received); - if (APR_STATUS_IS_TIMEUP(status)) - return SOCKET_TIMEOUT; - if (APR_STATUS_IS_EOF(status)) - return SOCKET_EOF; - CHECK_APR_SUCCESS(status); - return received; -} - -}} // namespace qpid::sys diff --git a/cpp/src/qpid/sys/apr/Thread.cpp b/cpp/src/qpid/sys/apr/Thread.cpp deleted file mode 100644 index b52d0e6ace..0000000000 --- a/cpp/src/qpid/sys/apr/Thread.cpp +++ /dev/null @@ -1,34 +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/sys/apr/Thread.h" -#include "qpid/sys/Runnable.h" - -using namespace qpid::sys; -using qpid::sys::Runnable; - -void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) { - reinterpret_cast<Runnable*>(data)->run(); - CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); - return NULL; -} - - diff --git a/cpp/src/qpid/sys/apr/Thread.h b/cpp/src/qpid/sys/apr/Thread.h deleted file mode 100644 index 6cc63db5c9..0000000000 --- a/cpp/src/qpid/sys/apr/Thread.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef _sys_apr_Thread_h -#define _sys_apr_Thread_h - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/apr/APRPool.h" -#include "qpid/sys/apr/APRBase.h" - -#include <apr_thread_proc.h> -#include <apr_portable.h> - -namespace qpid { -namespace sys { - -class Runnable; - -class Thread -{ - public: - inline static Thread current(); - - /** ID of current thread for logging. - * Workaround for broken Thread::current() in APR - */ - inline static long logId(); - - inline static void yield(); - - inline Thread(); - inline explicit Thread(qpid::sys::Runnable*); - inline explicit Thread(qpid::sys::Runnable&); - - inline void join(); - - inline long id(); - - private: - static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data); - inline Thread(apr_thread_t* t); - apr_thread_t* thread; -}; - -Thread::Thread() : thread(0) {} - -Thread::Thread(Runnable* runnable) { - CHECK_APR_SUCCESS( - apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get())); -} - -Thread::Thread(Runnable& runnable) { - CHECK_APR_SUCCESS( - apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get())); -} - -void Thread::join(){ - apr_status_t status; - if (thread != 0) - CHECK_APR_SUCCESS(apr_thread_join(&status, thread)); -} - -long Thread::id() { - return long(thread); -} - -/** ID of current thread for logging. - * Workaround for broken Thread::current() in APR - */ -long Thread::logId() { - return static_cast<long>(apr_os_thread_current()); -} - -Thread::Thread(apr_thread_t* t) : thread(t) {} - -Thread Thread::current(){ - apr_thread_t* thr; - apr_os_thread_t osthr = apr_os_thread_current(); - CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get())); - return Thread(thr); -} - -void Thread::yield() -{ - apr_thread_yield(); -} - -}} -#endif /*!_sys_apr_Thread_h*/ diff --git a/cpp/src/qpid/sys/posix/AsynchIO.cpp b/cpp/src/qpid/sys/posix/AsynchIO.cpp index a1c161b596..01ff8b6bfa 100644 --- a/cpp/src/qpid/sys/posix/AsynchIO.cpp +++ b/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -23,6 +23,7 @@ #include "qpid/sys/Socket.h" #include "qpid/sys/SocketAddress.h" #include "qpid/sys/Poller.h" +#include "qpid/sys/Probes.h" #include "qpid/sys/DispatchHandle.h" #include "qpid/sys/Time.h" #include "qpid/log/Statement.h" @@ -40,7 +41,9 @@ #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> -using namespace qpid::sys; +namespace qpid { +namespace sys { +namespace posix { namespace { @@ -70,10 +73,6 @@ __thread int64_t threadMaxIoTimeNs = 2 * 1000000; // start at 2ms /* * Asynch Acceptor */ -namespace qpid { -namespace sys { -namespace posix { - class AsynchAcceptor : public qpid::sys::AsynchAcceptor { public: AsynchAcceptor(const Socket& s, AsynchAcceptor::Callback callback); @@ -423,9 +422,12 @@ AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { void AsynchIO::readable(DispatchHandle& h) { if (readingStopped) { // We have been flow controlled. + QPID_PROBE1(asynchio_read_flowcontrolled, &h); return; } AbsTime readStartTime = AbsTime::now(); + size_t total = 0; + int readCalls = 0; do { // (Try to) get a buffer if (!bufferQueue.empty()) { @@ -436,23 +438,29 @@ void AsynchIO::readable(DispatchHandle& h) { errno = 0; int readCount = buff->byteCount-buff->dataCount; int rc = socket.read(buff->bytes + buff->dataCount, readCount); + int64_t duration = Duration(readStartTime, AbsTime::now()); + ++readCalls; if (rc > 0) { buff->dataCount += rc; threadReadTotal += rc; + total += rc; readCallback(*this, buff); if (readingStopped) { // We have been flow controlled. + QPID_PROBE4(asynchio_read_finished_flowcontrolled, &h, duration, total, readCalls); break; } if (rc != readCount) { // If we didn't fill the read buffer then time to stop reading + QPID_PROBE4(asynchio_read_finished_done, &h, duration, total, readCalls); break; } // Stop reading if we've overrun our timeslot - if (Duration(readStartTime, AbsTime::now()) > threadMaxIoTimeNs) { + if ( duration > threadMaxIoTimeNs) { + QPID_PROBE4(asynchio_read_finished_maxtime, &h, duration, total, readCalls); break; } @@ -461,6 +469,7 @@ void AsynchIO::readable(DispatchHandle& h) { bufferQueue.push_front(buff); assert(buff); + QPID_PROBE5(asynchio_read_finished_error, &h, duration, total, readCalls, errno); // Eof or other side has gone away if (rc == 0 || errno == ECONNRESET) { eofCallback(*this); @@ -486,6 +495,7 @@ void AsynchIO::readable(DispatchHandle& h) { // If we still have no buffers we can't do anything more if (bufferQueue.empty()) { h.unwatchRead(); + QPID_PROBE4(asynchio_read_finished_nobuffers, &h, Duration(readStartTime, AbsTime::now()), total, readCalls); break; } @@ -501,6 +511,8 @@ void AsynchIO::readable(DispatchHandle& h) { */ void AsynchIO::writeable(DispatchHandle& h) { AbsTime writeStartTime = AbsTime::now(); + size_t total = 0; + int writeCalls = 0; do { // See if we've got something to write if (!writeQueue.empty()) { @@ -510,14 +522,18 @@ void AsynchIO::writeable(DispatchHandle& h) { errno = 0; assert(buff->dataStart+buff->dataCount <= buff->byteCount); int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount); + int64_t duration = Duration(writeStartTime, AbsTime::now()); + ++writeCalls; if (rc >= 0) { threadWriteTotal += rc; + total += rc; // If we didn't write full buffer put rest back if (rc != buff->dataCount) { buff->dataStart += rc; buff->dataCount -= rc; writeQueue.push_back(buff); + QPID_PROBE4(asynchio_write_finished_done, &h, duration, total, writeCalls); break; } @@ -525,12 +541,15 @@ void AsynchIO::writeable(DispatchHandle& h) { queueReadBuffer(buff); // Stop writing if we've overrun our timeslot - if (Duration(writeStartTime, AbsTime::now()) > threadMaxIoTimeNs) { + if (duration > threadMaxIoTimeNs) { + QPID_PROBE4(asynchio_write_finished_maxtime, &h, duration, total, writeCalls); break; } } else { // Put buffer back writeQueue.push_back(buff); + QPID_PROBE5(asynchio_write_finished_error, &h, duration, total, writeCalls, errno); + if (errno == ECONNRESET || errno == EPIPE) { // Just stop watching for write here - we'll get a // disconnect callback soon enough @@ -548,9 +567,13 @@ void AsynchIO::writeable(DispatchHandle& h) { } } } else { + int64_t duration = Duration(writeStartTime, AbsTime::now()); + (void) duration; // force duration to be used if no probes are compiled + // If we're waiting to close the socket then can do it now as there is nothing to write if (queuedClose) { close(h); + QPID_PROBE4(asynchio_write_finished_closed, &h, duration, total, writeCalls); break; } // Fd is writable, but nothing to write @@ -567,6 +590,7 @@ void AsynchIO::writeable(DispatchHandle& h) { // desired rewatchWrite so we correct that here if (writePending) h.rewatchWrite(); + QPID_PROBE4(asynchio_write_finished_nodata, &h, duration, total, writeCalls); break; } } diff --git a/cpp/src/qpid/sys/posix/PollableCondition.cpp b/cpp/src/qpid/sys/posix/PollableCondition.cpp index b22a615a54..abff8a5be8 100644 --- a/cpp/src/qpid/sys/posix/PollableCondition.cpp +++ b/cpp/src/qpid/sys/posix/PollableCondition.cpp @@ -1,6 +1,3 @@ -#ifndef QPID_SYS_LINUX_POLLABLECONDITION_CPP -#define QPID_SYS_LINUX_POLLABLECONDITION_CPP - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -120,5 +117,3 @@ void PollableCondition::set() { impl->set(); } void PollableCondition::clear() { impl->clear(); } }} // namespace qpid::sys - -#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/ diff --git a/cpp/src/qpid/sys/ssl/SslIo.cpp b/cpp/src/qpid/sys/ssl/SslIo.cpp index 73f15617dc..2a7cf16923 100644 --- a/cpp/src/qpid/sys/ssl/SslIo.cpp +++ b/cpp/src/qpid/sys/ssl/SslIo.cpp @@ -37,8 +37,9 @@ #include <boost/bind.hpp> -using namespace qpid::sys; -using namespace qpid::sys::ssl; +namespace qpid { +namespace sys { +namespace ssl { namespace { @@ -448,3 +449,5 @@ SecuritySettings SslIO::getSecuritySettings() { settings.authid = socket.getClientAuthId(); return settings; } + +}}} diff --git a/cpp/src/qpid/sys/windows/AsynchIO.cpp b/cpp/src/qpid/sys/windows/AsynchIO.cpp index 30378d4c5f..ae53414e52 100644 --- a/cpp/src/qpid/sys/windows/AsynchIO.cpp +++ b/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -291,6 +291,8 @@ private: volatile LONG opsInProgress; // Is there a write in progress? volatile bool writeInProgress; + // Or a read? + volatile bool readInProgress; // Deletion requested, but there are callbacks in progress. volatile bool queuedDelete; // Socket close requested, but there are operations in progress. @@ -344,6 +346,11 @@ private: * Called when there's a completion to process. */ void completion(AsynchIoResult *result); + + /** + * Helper function to facilitate the close operation + */ + void cancelRead(); }; // This is used to encapsulate pure callbacks into a handle @@ -372,6 +379,7 @@ AsynchIO::AsynchIO(const Socket& s, socket(s), opsInProgress(0), writeInProgress(false), + readInProgress(false), queuedDelete(false), queuedClose(false), working(false) { @@ -389,21 +397,24 @@ AsynchIO::~AsynchIO() { } void AsynchIO::queueForDeletion() { - queuedDelete = true; - if (opsInProgress > 0) { - QPID_LOG(info, "Delete AsynchIO queued; ops in progress"); - // AsynchIOHandler calls this then deletes itself; don't do any more - // callbacks. - readCallback = 0; - eofCallback = 0; - disCallback = 0; - closedCallback = 0; - emptyCallback = 0; - idleCallback = 0; - } - else { - delete this; + { + ScopedLock<Mutex> l(completionLock); + assert(!queuedDelete); + queuedDelete = true; + if (working || opsInProgress > 0) { + QPID_LOG(info, "Delete AsynchIO queued; ops in progress"); + // AsynchIOHandler calls this then deletes itself; don't do any more + // callbacks. + readCallback = 0; + eofCallback = 0; + disCallback = 0; + closedCallback = 0; + emptyCallback = 0; + idleCallback = 0; + return; + } } + delete this; } void AsynchIO::start(Poller::shared_ptr poller0) { @@ -451,9 +462,14 @@ void AsynchIO::notifyPendingWrite() { } void AsynchIO::queueWriteClose() { - queuedClose = true; - if (!writeInProgress) - notifyPendingWrite(); + { + ScopedLock<Mutex> l(completionLock); + queuedClose = true; + if (working || writeInProgress) + // no need to summon an IO thread + return; + } + notifyPendingWrite(); } bool AsynchIO::writeQueueEmpty() { @@ -466,7 +482,7 @@ bool AsynchIO::writeQueueEmpty() { * called when the read is complete and data is available. */ void AsynchIO::startReading() { - if (queuedDelete) + if (queuedDelete || queuedClose) return; // (Try to) get a buffer; look on the front since there may be an @@ -489,6 +505,7 @@ void AsynchIO::startReading() { readCount); DWORD bytesReceived = 0, flags = 0; InterlockedIncrement(&opsInProgress); + readInProgress = true; int status = WSARecv(toSocketHandle(socket), const_cast<LPWSABUF>(result->getWSABUF()), 1, &bytesReceived, @@ -616,17 +633,19 @@ void AsynchIO::close(void) { void AsynchIO::readComplete(AsynchReadResult *result) { int status = result->getStatus(); size_t bytes = result->getTransferred(); + readInProgress = false; if (status == 0 && bytes > 0) { - bool restartRead = true; // May not if receiver doesn't want more if (readCallback) readCallback(*this, result->getBuff()); - if (restartRead) - startReading(); + startReading(); } else { // No data read, so put the buffer back. It may be partially filled, // so "unread" it back to the front of the queue. unread(result->getBuff()); + if (queuedClose) { + return; // Expected from cancelRead() + } notifyEof(); if (status != 0) { @@ -682,6 +701,8 @@ void AsynchIO::writeComplete(AsynchWriteResult *result) { } void AsynchIO::completion(AsynchIoResult *result) { + bool closing = false; + bool deleting = false; { ScopedLock<Mutex> l(completionLock); if (working) { @@ -713,6 +734,8 @@ void AsynchIO::completion(AsynchIoResult *result) { delete result; result = 0; InterlockedDecrement(&opsInProgress); + if (queuedClose && opsInProgress == 1 && readInProgress) + cancelRead(); } // Lock is held again. if (completionQueue.empty()) @@ -721,17 +744,40 @@ void AsynchIO::completion(AsynchIoResult *result) { completionQueue.pop(); } working = false; + if (opsInProgress == 0) { + closing = queuedClose; + deleting = queuedDelete; + } } // Lock released; ok to close if ops are done and close requested. // Layer above will call back to queueForDeletion() if it hasn't // already been done. If it already has, go ahead and delete. - if (opsInProgress == 0) { - if (queuedClose) - // close() may cause a delete; don't trust 'this' on return - close(); - else if (queuedDelete) - delete this; + if (deleting) + delete this; + else if (closing) + // close() may cause a delete; don't trust 'this' on return + close(); +} + +/* + * NOTE - this method must be called in the same context as other completions, + * so that the resulting readComplete, and final AsynchIO::close() is serialized + * after this method returns. + */ +void AsynchIO::cancelRead() { + if (queuedDelete) + return; // socket already deleted + else { + ScopedLock<Mutex> l(completionLock);; + if (!completionQueue.empty()) + return; // process it; come back later if necessary } + // Cancel outstanding read and force to completion. Otherwise, on a faulty + // physical link, the pending read can remain uncompleted indefinitely. + // Draining the pending read will result in the official close (and + // notifyClosed). CancelIoEX() is the natural choice, but not available in + // XP, so we make do with closesocket(). + socket.close(); } } // namespace windows diff --git a/cpp/src/qpid/sys/windows/PollableCondition.cpp b/cpp/src/qpid/sys/windows/PollableCondition.cpp index 6a1d9045b4..bb637be0a6 100644 --- a/cpp/src/qpid/sys/windows/PollableCondition.cpp +++ b/cpp/src/qpid/sys/windows/PollableCondition.cpp @@ -1,6 +1,3 @@ -#ifndef QPID_SYS_WINDOWS_POLLABLECONDITION_CPP -#define QPID_SYS_WINDOWS_POLLABLECONDITION_CPP - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -110,5 +107,3 @@ void PollableCondition::clear() { } }} // namespace qpid::sys - -#endif /*!QPID_SYS_WINDOWS_POLLABLECONDITION_CPP*/ diff --git a/cpp/src/qpid/sys/windows/Socket.cpp b/cpp/src/qpid/sys/windows/Socket.cpp index 1fa4768329..b085f67539 100644 --- a/cpp/src/qpid/sys/windows/Socket.cpp +++ b/cpp/src/qpid/sys/windows/Socket.cpp @@ -32,6 +32,9 @@ #include <winsock2.h> +namespace qpid { +namespace sys { + // Need to initialize WinSock. Ideally, this would be a singleton or embedded // in some one-time initialization function. I tried boost singleton and could // not get it to compile (and others located in google had the same problem). @@ -76,13 +79,6 @@ protected: static WinSockSetup setup; -} /* namespace */ - -namespace qpid { -namespace sys { - -namespace { - std::string getName(SOCKET fd, bool local) { ::sockaddr_storage name_s; // big enough for any socket address diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp index 11a3389e45..25cc94b290 100644 --- a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp +++ b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp @@ -38,6 +38,10 @@ #include <queue> #include <boost/bind.hpp> +namespace qpid { +namespace sys { +namespace windows { + namespace { /* @@ -66,10 +70,6 @@ namespace { }; } -namespace qpid { -namespace sys { -namespace windows { - SslAsynchIO::SslAsynchIO(const qpid::sys::Socket& s, CredHandle hCred, ReadCallback rCb, diff --git a/cpp/src/qpid/xml/XmlExchange.cpp b/cpp/src/qpid/xml/XmlExchange.cpp index b7ff5d211d..01770e22a6 100644 --- a/cpp/src/qpid/xml/XmlExchange.cpp +++ b/cpp/src/qpid/xml/XmlExchange.cpp @@ -283,8 +283,10 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F // But for very large messages, if all these queries are on the first part of the data, // it could still be a big win. -void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* args) +void XmlExchange::route(Deliverable& msg) { + const string& routingKey = msg.getMessage().getRoutingKey(); + const FieldTable* args = msg.getMessage().getApplicationHeaders(); PreRoute pr(msg, this); try { XmlBinding::vector::ConstPtr p; diff --git a/cpp/src/qpid/xml/XmlExchange.h b/cpp/src/qpid/xml/XmlExchange.h index 958bad4931..9ef389d9bf 100644 --- a/cpp/src/qpid/xml/XmlExchange.h +++ b/cpp/src/qpid/xml/XmlExchange.h @@ -82,7 +82,7 @@ class XmlExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg); virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args); diff --git a/cpp/src/qpidd.cpp b/cpp/src/qpidd.cpp index a0e329ca9d..b5686c6ab8 100644 --- a/cpp/src/qpidd.cpp +++ b/cpp/src/qpidd.cpp @@ -29,6 +29,9 @@ #include <memory> using namespace std; +namespace qpid { +namespace broker { + auto_ptr<QpiddOptions> options; // Broker real entry; various system-invoked entrypoints call here. @@ -87,3 +90,4 @@ int run_broker(int argc, char *argv[], bool hidden) } return 1; } +}} diff --git a/cpp/src/qpidd.h b/cpp/src/qpidd.h index a3150a2737..f7f84d11da 100644 --- a/cpp/src/qpidd.h +++ b/cpp/src/qpidd.h @@ -26,6 +26,9 @@ #include <memory> +namespace qpid { +namespace broker { + // BootstrapOptions is a minimal subset of options used for a pre-parse // of the command line to discover which plugin modules need to be loaded. // The pre-parse is necessary because plugin modules may supply their own @@ -70,4 +73,5 @@ public: // Broker real entry; various system-invoked entrypoints call here. int run_broker(int argc, char *argv[], bool hidden = false); +}} #endif /*!QPID_H*/ diff --git a/cpp/src/tests/Array.cpp b/cpp/src/tests/Array.cpp index 7622b89d15..8ce7615162 100644 --- a/cpp/src/tests/Array.cpp +++ b/cpp/src/tests/Array.cpp @@ -58,7 +58,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) BOOST_CHECK_EQUAL(a, b); std::vector<std::string> data2; - b.collect(data2); + std::transform(b.begin(), b.end(), std::back_inserter(data2), Array::get<std::string, Array::ValuePtr>); //BOOST_CHECK_EQUAL(data, data2); BOOST_CHECK(data == data2); } @@ -74,7 +74,7 @@ QPID_AUTO_TEST_CASE(testArrayAssignment) BOOST_CHECK_EQUAL(a, b); } std::vector<std::string> data2; - b.collect(data2); + std::transform(b.begin(), b.end(), std::back_inserter(data2), Array::get<std::string, Array::ValuePtr>); //BOOST_CHECK_EQUAL(data, data2); BOOST_CHECK(data == data2); } diff --git a/cpp/src/tests/CMakeLists.txt b/cpp/src/tests/CMakeLists.txt index 75ecffe866..5979ce42ae 100644 --- a/cpp/src/tests/CMakeLists.txt +++ b/cpp/src/tests/CMakeLists.txt @@ -23,7 +23,11 @@ include (CTest) # Make sure that everything get built before the tests # Need to create a var with all the necessary top level targets -add_definitions(-DBOOST_TEST_DYN_LINK) +# If we're linking Boost for DLLs, turn that on for the unit test too. +if (QPID_LINK_BOOST_DYNAMIC) + add_definitions(-DBOOST_TEST_DYN_LINK) +endif (QPID_LINK_BOOST_DYNAMIC) + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) include (FindPythonInterp) @@ -34,8 +38,14 @@ set (abs_builddir ${CMAKE_CURRENT_BINARY_DIR}) set (abs_top_srcdir ${CMAKE_SOURCE_DIR}) set (abs_top_builddir ${CMAKE_BINARY_DIR}) set (builddir_lib_suffix "") -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.sh.in - ${CMAKE_CURRENT_BINARY_DIR}/test_env.sh) + +if (CMAKE_SYSTEM_NAME STREQUAL Windows) + configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.ps1.in + ${CMAKE_CURRENT_BINARY_DIR}/test_env.ps1) +else (CMAKE_SYSTEM_NAME STREQUAL Windows) + configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.sh.in + ${CMAKE_CURRENT_BINARY_DIR}/test_env.sh) +endif (CMAKE_SYSTEM_NAME STREQUAL Windows) # If valgrind is selected in the configuration step, set up the path to it @@ -145,13 +155,6 @@ set(unit_tests_to_build mark_as_advanced(unit_tests_to_build) -# Disabled till we move to amqp_0_10 codec. -# amqp_0_10/serialize.cpp allSegmentTypes.h \ -# amqp_0_10/ProxyTemplate.cpp \ -# amqp_0_10/apply.cpp \ -# amqp_0_10/Map.cpp \ -# amqp_0_10/handlers.cpp - add_executable (unit_test unit_test ${unit_tests_to_build} ${platform_test_additions}) target_link_libraries (unit_test @@ -320,7 +323,7 @@ if (BUILD_MSSQL) add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL) endif (BUILD_MSSQL) if (BUILD_MSCLFS) - add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL-CLFS) + add_test (store_tests_clfs ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL-CLFS) endif (BUILD_MSCLFS) endif (PYTHON_EXECUTABLE) diff --git a/cpp/src/tests/ExchangeTest.cpp b/cpp/src/tests/ExchangeTest.cpp index fe72f42a46..2fb284741a 100644 --- a/cpp/src/tests/ExchangeTest.cpp +++ b/cpp/src/tests/ExchangeTest.cpp @@ -60,10 +60,10 @@ QPID_AUTO_TEST_CASE(testMe) queue.reset(); queue2.reset(); - intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", false, "id")); + intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "abc", false, "id")); DeliverableMessage msg(msgPtr); - topic.route(msg, "abc", 0); - direct.route(msg, "abc", 0); + topic.route(msg); + direct.route(msg); } @@ -187,17 +187,17 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) { DirectExchange direct("direct1", false, args); - intrusive_ptr<Message> msg1 = cmessage("e", "A"); - intrusive_ptr<Message> msg2 = cmessage("e", "B"); - intrusive_ptr<Message> msg3 = cmessage("e", "C"); + intrusive_ptr<Message> msg1 = cmessage("e", "abc"); + intrusive_ptr<Message> msg2 = cmessage("e", "abc"); + intrusive_ptr<Message> msg3 = cmessage("e", "abc"); DeliverableMessage dmsg1(msg1); DeliverableMessage dmsg2(msg2); DeliverableMessage dmsg3(msg3); - direct.route(dmsg1, "abc", 0); - direct.route(dmsg2, "abc", 0); - direct.route(dmsg3, "abc", 0); + direct.route(dmsg1); + direct.route(dmsg2); + direct.route(dmsg3); BOOST_CHECK_EQUAL(1, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); BOOST_CHECK_EQUAL(2, msg2->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); @@ -208,22 +208,24 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) TopicExchange topic ("topic1", false, args); // check other exchanges, that they preroute - intrusive_ptr<Message> msg4 = cmessage("e", "A"); - intrusive_ptr<Message> msg5 = cmessage("e", "B"); - intrusive_ptr<Message> msg6 = cmessage("e", "C"); + intrusive_ptr<Message> msg4 = cmessage("e", "abc"); + intrusive_ptr<Message> msg5 = cmessage("e", "abc"); + + // Need at least empty header for the HeadersExchange to route at all + msg5->insertCustomProperty("", ""); + intrusive_ptr<Message> msg6 = cmessage("e", "abc"); DeliverableMessage dmsg4(msg4); DeliverableMessage dmsg5(msg5); DeliverableMessage dmsg6(msg6); - fanout.route(dmsg4, "abc", 0); + fanout.route(dmsg4); BOOST_CHECK_EQUAL(1, msg4->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - FieldTable headers; - header.route(dmsg5, "abc", &headers); + header.route(dmsg5); BOOST_CHECK_EQUAL(1, msg5->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - topic.route(dmsg6, "abc", 0); + topic.route(dmsg6); BOOST_CHECK_EQUAL(1, msg6->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); direct.encode(buffer); } @@ -233,9 +235,9 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) buffer.reset(); DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer); - intrusive_ptr<Message> msg1 = cmessage("e", "A"); + intrusive_ptr<Message> msg1 = cmessage("e", "abc"); DeliverableMessage dmsg1(msg1); - exch_dec->route(dmsg1, "abc", 0); + exch_dec->route(dmsg1); BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); @@ -260,10 +262,10 @@ QPID_AUTO_TEST_CASE(testIVEOption) args2.setString("x-match", "any"); args2.setString("a", "abc"); - direct.route(dmsg1, "abc", 0); - fanout.route(dmsg1, "abc", 0); - header.route(dmsg1, "abc", &args2); - topic.route(dmsg1, "abc", 0); + direct.route(dmsg1); + fanout.route(dmsg1); + header.route(dmsg1); + topic.route(dmsg1); Queue::shared_ptr queue(new Queue("queue", true)); Queue::shared_ptr queue1(new Queue("queue1", true)); Queue::shared_ptr queue2(new Queue("queue2", true)); diff --git a/cpp/src/tests/FieldTable.cpp b/cpp/src/tests/FieldTable.cpp index fe2a14ec03..c79d110ae4 100644 --- a/cpp/src/tests/FieldTable.cpp +++ b/cpp/src/tests/FieldTable.cpp @@ -19,11 +19,11 @@ * */ #include <iostream> +#include <algorithm> #include "qpid/framing/Array.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/List.h" -#include "qpid/sys/alloca.h" #include "unit_test.h" @@ -127,7 +127,7 @@ QPID_AUTO_TEST_CASE(testNestedValues) BOOST_CHECK(string("B") == b.getAsString("id")); a.getArray("C", c); std::vector<std::string> items; - c.collect(items); + std::transform(c.begin(), c.end(), std::back_inserter(items), Array::get<std::string, Array::ValuePtr>); BOOST_CHECK((uint) 2 == items.size()); BOOST_CHECK(string("one") == items[0]); BOOST_CHECK(string("two") == items[1]); diff --git a/cpp/src/tests/Frame.cpp b/cpp/src/tests/Frame.cpp index 1270eabba3..cfcfde04a7 100644 --- a/cpp/src/tests/Frame.cpp +++ b/cpp/src/tests/Frame.cpp @@ -30,7 +30,6 @@ QPID_AUTO_TEST_SUITE(FrameTestSuite) using namespace std; using namespace qpid::framing; -using namespace boost; QPID_AUTO_TEST_CASE(testContentBody) { Frame f(42, AMQContentBody("foobar")); diff --git a/cpp/src/tests/FramingTest.cpp b/cpp/src/tests/FramingTest.cpp index f8795316cc..2392b6fec4 100644 --- a/cpp/src/tests/FramingTest.cpp +++ b/cpp/src/tests/FramingTest.cpp @@ -25,6 +25,7 @@ #include "qpid/framing/all_method_bodies.h" #include "qpid/framing/amqp_framing.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include "unit_test.h" #include <boost/bind.hpp> diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am index 325019fb22..66d2cdd5d5 100644 --- a/cpp/src/tests/Makefile.am +++ b/cpp/src/tests/Makefile.am @@ -149,7 +149,7 @@ endif # Test programs that are installed and therefore built as part of make, not make check -qpidexectest_SCRIPTS += qpid-cpp-benchmark install_env.sh +qpidexectest_SCRIPTS += qpid-cpp-benchmark qpid-cluster-benchmark install_env.sh EXTRA_DIST += qpid-cpp-benchmark install_env.sh qpidexectest_PROGRAMS += receiver @@ -320,7 +320,7 @@ EXTRA_DIST += \ header_test.py \ ssl_test \ config.null \ - ais_check \ + cpg_check.sh.in \ run_federation_tests \ run_federation_sys_tests \ run_long_federation_sys_tests \ @@ -351,7 +351,9 @@ EXTRA_DIST += \ topictest.ps1 \ run_queue_flow_limit_tests \ run_msg_group_tests \ - ipv6_test + ipv6_test \ + ha_tests.py \ + test_env.ps1.in check_LTLIBRARIES += libdlclose_noop.la libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir) @@ -366,9 +368,15 @@ LONG_TESTS+=start_broker \ fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test \ run_msg_group_tests_soak \ stop_broker \ - run_long_federation_sys_tests \ - run_failover_soak reliable_replication_test \ - federated_cluster_test_with_node_failure + run_long_federation_sys_tests + +if HAVE_LIBCPG + +LONG_TESTS+= federated_cluster_test_with_node_failure \ + run_failover_soak \ + reliable_replication_test + +endif HAVE_LIBCPG EXTRA_DIST+= \ fanout_perftest \ @@ -379,7 +387,8 @@ EXTRA_DIST+= \ reliable_replication_test \ federated_cluster_test_with_node_failure \ sasl_test_setup.sh \ - run_msg_group_tests_soak + run_msg_group_tests_soak \ + qpidd-empty.conf check-long: $(MAKE) check TESTS="$(LONG_TESTS)" VALGRIND= diff --git a/cpp/src/tests/MessagingSessionTests.cpp b/cpp/src/tests/MessagingSessionTests.cpp index 313e8b3132..968d55fd45 100644 --- a/cpp/src/tests/MessagingSessionTests.cpp +++ b/cpp/src/tests/MessagingSessionTests.cpp @@ -1139,6 +1139,13 @@ QPID_AUTO_TEST_CASE(testHeadersExchange) } } +QPID_AUTO_TEST_CASE(testLargeRoutingKey) +{ + MessagingFixture fix; + std::string address = "amq.direct/" + std::string(300, 'x');//routing/binding key can be at most 225 chars in 0-10 + BOOST_CHECK_THROW(fix.session.createReceiver(address), qpid::messaging::MessagingException); +} + QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/cpp/src/tests/QueueFlowLimitTest.cpp b/cpp/src/tests/QueueFlowLimitTest.cpp index 8a6923fb09..bd868398f8 100644 --- a/cpp/src/tests/QueueFlowLimitTest.cpp +++ b/cpp/src/tests/QueueFlowLimitTest.cpp @@ -27,6 +27,7 @@ #include "qpid/broker/QueueFlowLimit.h" #include "qpid/sys/Time.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include "MessageUtils.h" #include "BrokerFixture.h" diff --git a/cpp/src/tests/QueueTest.cpp b/cpp/src/tests/QueueTest.cpp index bb4f7b9f4b..0058aa5133 100644 --- a/cpp/src/tests/QueueTest.cpp +++ b/cpp/src/tests/QueueTest.cpp @@ -254,8 +254,8 @@ QPID_AUTO_TEST_CASE(testBound){ //ensure the remaining exchanges don't still have the queue bound to them: FailOnDeliver deliverable; - exchange1->route(deliverable, key, &args); - exchange3->route(deliverable, key, &args); + exchange1->route(deliverable); + exchange3->route(deliverable); } QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ @@ -1151,7 +1151,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg01 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg01(msg01); - sbtFanout1.route(dmsg01, "", 0); // Brings queue 1 to capacity limit + sbtFanout1.route(dmsg01); // Brings queue 1 to capacity limit msg01->tryReleaseContent(); BOOST_CHECK_EQUAL(msg01->isContentReleased(), false); BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); @@ -1160,7 +1160,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ DeliverableMessage dmsg02(msg02); { ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg02, "", 0), ResourceLimitExceededException); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg02), ResourceLimitExceededException); } msg02->tryReleaseContent(); BOOST_CHECK_EQUAL(msg02->isContentReleased(), false); @@ -1170,7 +1170,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ DeliverableMessage dmsg03(msg03); { ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg03, "", 0), ResourceLimitExceededException); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg03), ResourceLimitExceededException); } msg03->tryReleaseContent(); BOOST_CHECK_EQUAL(msg03->isContentReleased(), false); @@ -1180,7 +1180,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ DeliverableMessage dmsg04(msg04); { ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg04, "", 0), ResourceLimitExceededException); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg04), ResourceLimitExceededException); } msg04->tryReleaseContent(); BOOST_CHECK_EQUAL(msg04->isContentReleased(), false); @@ -1190,7 +1190,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ DeliverableMessage dmsg05(msg05); { ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg05, "", 0), ResourceLimitExceededException); + BOOST_CHECK_THROW(sbtFanout1.route(dmsg05), ResourceLimitExceededException); } msg05->tryReleaseContent(); BOOST_CHECK_EQUAL(msg05->isContentReleased(), false); @@ -1205,35 +1205,35 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg06 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg06(msg06); - sbdFanout2.route(dmsg06, "", 0); // Brings queue 2 to capacity limit + sbdFanout2.route(dmsg06); // Brings queue 2 to capacity limit msg06->tryReleaseContent(); BOOST_CHECK_EQUAL(msg06->isContentReleased(), false); BOOST_CHECK_EQUAL(1u, dq2->getMessageCount()); intrusive_ptr<Message> msg07 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg07(msg07); - sbdFanout2.route(dmsg07, "", 0); + sbdFanout2.route(dmsg07); msg07->tryReleaseContent(); BOOST_CHECK_EQUAL(msg07->isContentReleased(), true); BOOST_CHECK_EQUAL(2u, dq2->getMessageCount()); intrusive_ptr<Message> msg08 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content DeliverableMessage dmsg08(msg08); - sbdFanout2.route(dmsg08, "", 0); + sbdFanout2.route(dmsg08); msg08->tryReleaseContent(); BOOST_CHECK_EQUAL(msg08->isContentReleased(), true); BOOST_CHECK_EQUAL(3u, dq2->getMessageCount()); intrusive_ptr<Message> msg09 = mkMsg(testStore); // transient no content DeliverableMessage dmsg09(msg09); - sbdFanout2.route(dmsg09, "", 0); + sbdFanout2.route(dmsg09); msg09->tryReleaseContent(); BOOST_CHECK_EQUAL(msg09->isContentReleased(), true); BOOST_CHECK_EQUAL(4u, dq2->getMessageCount()); intrusive_ptr<Message> msg10 = mkMsg(testStore, "", true); // durable no content DeliverableMessage dmsg10(msg10); - sbdFanout2.route(dmsg10, "", 0); + sbdFanout2.route(dmsg10); msg10->tryReleaseContent(); BOOST_CHECK_EQUAL(msg10->isContentReleased(), true); BOOST_CHECK_EQUAL(5u, dq2->getMessageCount()); @@ -1253,7 +1253,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg11 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg11(msg11); - mbdFanout3.route(dmsg11, "", 0); // Brings queues 3 and 4 to capacity limit + mbdFanout3.route(dmsg11); // Brings queues 3 and 4 to capacity limit msg11->tryReleaseContent(); BOOST_CHECK_EQUAL(msg11->isContentReleased(), false); BOOST_CHECK_EQUAL(1u, dq3->getMessageCount()); @@ -1262,7 +1262,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg12 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg12(msg12); - mbdFanout3.route(dmsg12, "", 0); + mbdFanout3.route(dmsg12); msg12->tryReleaseContent(); BOOST_CHECK_EQUAL(msg12->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point! BOOST_CHECK_EQUAL(2u, dq3->getMessageCount()); @@ -1271,7 +1271,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg13 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content DeliverableMessage dmsg13(msg13); - mbdFanout3.route(dmsg13, "", 0); + mbdFanout3.route(dmsg13); msg13->tryReleaseContent(); BOOST_CHECK_EQUAL(msg13->isContentReleased(), true); BOOST_CHECK_EQUAL(3u, dq3->getMessageCount()); @@ -1280,7 +1280,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg14 = mkMsg(testStore); // transient no content DeliverableMessage dmsg14(msg14); - mbdFanout3.route(dmsg14, "", 0); + mbdFanout3.route(dmsg14); msg14->tryReleaseContent(); BOOST_CHECK_EQUAL(msg14->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point! BOOST_CHECK_EQUAL(4u, dq3->getMessageCount()); @@ -1289,7 +1289,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg15 = mkMsg(testStore, "", true); // durable no content DeliverableMessage dmsg15(msg15); - mbdFanout3.route(dmsg15, "", 0); + mbdFanout3.route(dmsg15); msg15->tryReleaseContent(); BOOST_CHECK_EQUAL(msg15->isContentReleased(), true); BOOST_CHECK_EQUAL(5u, dq3->getMessageCount()); @@ -1307,7 +1307,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg16 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg16(msg16); - mbdFanout3.route(dmsg16, "", 0); + mbdFanout3.route(dmsg16); msg16->tryReleaseContent(); BOOST_CHECK_EQUAL(msg16->isContentReleased(), false); BOOST_CHECK_EQUAL(6u, dq3->getMessageCount()); @@ -1316,7 +1316,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg17 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content DeliverableMessage dmsg17(msg17); - mbdFanout3.route(dmsg17, "", 0); + mbdFanout3.route(dmsg17); msg17->tryReleaseContent(); BOOST_CHECK_EQUAL(msg17->isContentReleased(), false); BOOST_CHECK_EQUAL(7u, dq3->getMessageCount()); @@ -1325,7 +1325,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg18 = mkMsg(testStore); // transient no content DeliverableMessage dmsg18(msg18); - mbdFanout3.route(dmsg18, "", 0); + mbdFanout3.route(dmsg18); msg18->tryReleaseContent(); BOOST_CHECK_EQUAL(msg18->isContentReleased(), false); BOOST_CHECK_EQUAL(8u, dq3->getMessageCount()); @@ -1334,7 +1334,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg19 = mkMsg(testStore, "", true); // durable no content DeliverableMessage dmsg19(msg19); - mbdFanout3.route(dmsg19, "", 0); + mbdFanout3.route(dmsg19); msg19->tryReleaseContent(); BOOST_CHECK_EQUAL(msg19->isContentReleased(), false); BOOST_CHECK_EQUAL(9u, dq3->getMessageCount()); @@ -1357,7 +1357,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg20 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg20(msg20); - mbmFanout4.route(dmsg20, "", 0); // Brings queue 7 to capacity limit + mbmFanout4.route(dmsg20); // Brings queue 7 to capacity limit msg20->tryReleaseContent(); BOOST_CHECK_EQUAL(msg20->isContentReleased(), false); BOOST_CHECK_EQUAL(1u, dq7->getMessageCount()); @@ -1366,7 +1366,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg21 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content DeliverableMessage dmsg21(msg21); - mbmFanout4.route(dmsg21, "", 0); + mbmFanout4.route(dmsg21); msg21->tryReleaseContent(); BOOST_CHECK_EQUAL(msg21->isContentReleased(), false); BOOST_CHECK_EQUAL(2u, dq7->getMessageCount()); // over limit @@ -1375,7 +1375,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg22 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content DeliverableMessage dmsg22(msg22); - mbmFanout4.route(dmsg22, "", 0); + mbmFanout4.route(dmsg22); msg22->tryReleaseContent(); BOOST_CHECK_EQUAL(msg22->isContentReleased(), false); BOOST_CHECK_EQUAL(3u, dq7->getMessageCount()); // over limit @@ -1384,7 +1384,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg23 = mkMsg(testStore); // transient no content DeliverableMessage dmsg23(msg23); - mbmFanout4.route(dmsg23, "", 0); + mbmFanout4.route(dmsg23); msg23->tryReleaseContent(); BOOST_CHECK_EQUAL(msg23->isContentReleased(), false); BOOST_CHECK_EQUAL(4u, dq7->getMessageCount()); // over limit @@ -1393,7 +1393,7 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ intrusive_ptr<Message> msg24 = mkMsg(testStore, "", true); // durable no content DeliverableMessage dmsg24(msg24); - mbmFanout4.route(dmsg24, "", 0); + mbmFanout4.route(dmsg24); msg24->tryReleaseContent(); BOOST_CHECK_EQUAL(msg24->isContentReleased(), false); BOOST_CHECK_EQUAL(5u, dq7->getMessageCount()); // over limit diff --git a/cpp/src/tests/RefCounted.cpp b/cpp/src/tests/RefCounted.cpp index e4c1da5696..3ac3895322 100644 --- a/cpp/src/tests/RefCounted.cpp +++ b/cpp/src/tests/RefCounted.cpp @@ -21,15 +21,15 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(RefCountedTestSuiteTestSuite) using boost::intrusive_ptr; using namespace std; using namespace qpid; -namespace qpid { -namespace tests { - struct CountMe : public RefCounted { static int instances; CountMe() { ++instances; } diff --git a/cpp/src/tests/SessionState.cpp b/cpp/src/tests/SessionState.cpp index 3be9bb0cbc..1cf3415484 100644 --- a/cpp/src/tests/SessionState.cpp +++ b/cpp/src/tests/SessionState.cpp @@ -34,7 +34,6 @@ namespace tests { QPID_AUTO_TEST_SUITE(SessionStateTestSuite) using namespace std; -using namespace boost; using namespace qpid::framing; // ================================================================ diff --git a/cpp/src/tests/StringUtils.cpp b/cpp/src/tests/StringUtils.cpp index 6a19119288..c50287a4f4 100644 --- a/cpp/src/tests/StringUtils.cpp +++ b/cpp/src/tests/StringUtils.cpp @@ -23,9 +23,11 @@ #include "unit_test.h" +namespace qpid { +namespace tests { + QPID_AUTO_TEST_SUITE(StringUtilsTestSuite) -using namespace qpid; using std::string; QPID_AUTO_TEST_CASE(testSplit_general) @@ -75,3 +77,5 @@ QPID_AUTO_TEST_CASE(testSplit_empty) } QPID_AUTO_TEST_SUITE_END() + +}} diff --git a/cpp/src/tests/Uuid.cpp b/cpp/src/tests/Uuid.cpp index 0195455ca3..f85a297adc 100644 --- a/cpp/src/tests/Uuid.cpp +++ b/cpp/src/tests/Uuid.cpp @@ -48,7 +48,7 @@ QPID_AUTO_TEST_CASE(testUuidCtor) { for_each(uuids.begin(), uuids.end(), unique); } -boost::array<uint8_t, 16> sample = {{'\x1b', '\x4e', '\x28', '\xba', '\x2f', '\xa1', '\x11', '\xd2', '\x88', '\x3f', '\xb9', '\xa7', '\x61', '\xbd', '\xe3', '\xfb'}}; +boost::array<uint8_t, 16> sample = {{0x1b, 0x4e, 0x28, 0xba, 0x2f, 0xa1, 0x11, 0xd2, 0x88, 0x3f, 0xb9, 0xa7, 0x61, 0xbd, 0xe3, 0xfb}}; const string sampleStr("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb"); const string zeroStr("00000000-0000-0000-0000-000000000000"); diff --git a/cpp/src/tests/acl.py b/cpp/src/tests/acl.py index 65d5242e51..720b3b4216 100755 --- a/cpp/src/tests/acl.py +++ b/cpp/src/tests/acl.py @@ -7,9 +7,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,13 +31,13 @@ import qpid.messaging class ACLFile: def __init__(self, policy='data_dir/policy.acl'): self.f = open(policy,'w') - + def write(self,line): self.f.write(line) - + def close(self): self.f.close() - + class ACLTests(TestBase010): def get_session(self, user, passwd): @@ -47,9 +47,44 @@ class ACLTests(TestBase010): connection.start() return connection.session(str(uuid4())) + def port_i(self): + return int(self.defines["port-i"]) + + def port_u(self): + return int(self.defines["port-u"]) + + def get_session_by_port(self, user, passwd, byPort): + socket = connect(self.broker.host, byPort) + connection = Connection (sock=socket, username=user, password=passwd, + mechanism="PLAIN") + connection.start() + return connection.session(str(uuid4())) + def reload_acl(self): - acl = self.qmf.getObjects(_class="acl")[0] - return acl.reloadACLFile() + result = None + try: + self.broker_access.reloadAclFile() + except Exception, e: + result = str(e) + return result + + def acl_lookup(self, userName, action, aclObj, aclObjName, propMap): + result = {} + try: + result = self.broker_access.acl_lookup(userName, action, aclObj, aclObjName, propMap) + except Exception, e: + result['text'] = str(e) + result['result'] = str(e) + return result + + def acl_lookupPublish(self, userName, exchange, key): + result = {} + try: + result = self.broker_access.acl_lookupPublish(userName, exchange, key) + except Exception, e: + result['text'] = str(e) + result['result'] = str(e) + return result def get_acl_file(self): return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl")) @@ -59,7 +94,7 @@ class ACLTests(TestBase010): aclf.write('acl allow all all\n') aclf.close() TestBase010.setUp(self) - self.startQmf() + self.startBrokerAccess() self.reload_acl() def tearDown(self): @@ -69,10 +104,41 @@ class ACLTests(TestBase010): self.reload_acl() TestBase010.tearDown(self) + + def Lookup(self, userName, action, aclObj, aclObjName, propMap, expectedResult): + result = self.acl_lookup(userName, action, aclObj, aclObjName, propMap) + if (result['result'] != expectedResult): + suffix = ', [ERROR: Expected= ' + expectedResult + if (result['result'] is None): + suffix = suffix + ', Exception= ' + result['text'] + ']' + else: + suffix = suffix + ', Actual= ' + result['result'] + ']' + self.fail('Lookup: name=' + userName + ', action=' + action + ', aclObj=' + aclObj + ', aclObjName=' + aclObjName + ', propertyMap=' + str(propMap) + suffix) + + + def LookupPublish(self, userName, exchName, keyName, expectedResult): + result = self.acl_lookupPublish(userName, exchName, keyName) + if (result['result'] != expectedResult): + if (result['result'] is None): + suffix = suffix + ', Exception= ' + result['text'] + ']' + else: + suffix = suffix + ', Actual= ' + result['result'] + ']' + self.fail('LookupPublish: name=' + userName + ', exchange=' + exchName + ', key=' + keyName + suffix) + + def AllBut(self, allList, removeList): + tmpList = allList[:] + for item in removeList: + try: + tmpList.remove(item) + except Exception, e: + self.fail("ERROR in AllBut() \nallList = %s \nremoveList = %s \nerror = %s " \ + % (allList, removeList, e)) + return tmpList + #===================================== # ACL general tests - #===================================== - + #===================================== + def test_deny_mode(self): """ Test the deny all mode @@ -81,12 +147,12 @@ class ACLTests(TestBase010): aclf.write('acl allow anonymous all all\n') aclf.write('acl allow bob@QPID create queue\n') aclf.write('acl deny all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') try: session.queue_declare(queue="deny_queue") @@ -94,13 +160,13 @@ class ACLTests(TestBase010): if (403 == e.args[0].error_code): self.fail("ACL should allow queue create request"); self.fail("Error during queue create request"); - + try: session.exchange_bind(exchange="amq.direct", queue="deny_queue", binding_key="routing_key") self.fail("ACL should deny queue bind request"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) - + self.assertEqual(403,e.args[0].error_code) + def test_allow_mode(self): """ Test the allow all mode @@ -108,12 +174,12 @@ class ACLTests(TestBase010): aclf = self.get_acl_file() aclf.write('acl deny bob@QPID bind exchange\n') aclf.write('acl allow all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') try: session.queue_declare(queue="allow_queue") @@ -121,18 +187,42 @@ class ACLTests(TestBase010): if (403 == e.args[0].error_code): self.fail("ACL should allow queue create request"); self.fail("Error during queue create request"); - + try: session.exchange_bind(exchange="amq.direct", queue="allow_queue", binding_key="routing_key") self.fail("ACL should deny queue bind request"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) - - + self.assertEqual(403,e.args[0].error_code) + + + def test_allow_mode_with_specfic_allow_override(self): + """ + Specific allow overrides a general deny + """ + aclf = self.get_acl_file() + aclf.write('group admins bob@QPID joe@QPID \n') + aclf.write('acl allow bob@QPID create queue \n') + aclf.write('acl deny admins create queue \n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + session = self.get_session('bob','bob') + + try: + session.queue_declare(queue='zed') + except qpid.session.SessionException, e: + if (403 == e.args[0].error_code): + self.fail("ACL should allow create queue request"); + + #===================================== # ACL file format tests - #===================================== - + #===================================== + def test_empty_groups(self): """ Test empty groups @@ -141,11 +231,11 @@ class ACLTests(TestBase010): aclf.write('acl group\n') aclf.write('acl group admins bob@QPID joe@QPID\n') aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() - if (result.text.find("Insufficient tokens for acl definition",0,len(result.text)) == -1): - self.fail("ACL Reader should reject the acl file due to empty group name") + aclf.close() + + result = self.reload_acl() + if (result.find("Insufficient tokens for acl definition",0,len(result)) == -1): + self.fail("ACL Reader should reject the acl file due to empty group name") def test_illegal_acl_formats(self): """ @@ -155,31 +245,31 @@ class ACLTests(TestBase010): aclf.write('acl group admins bob@QPID joe@QPID\n') aclf.write('acl allow all all') aclf.close() - - result = self.reload_acl() - if (result.text.find("Unknown ACL permission",0,len(result.text)) == -1): - self.fail(result) - + + result = self.reload_acl() + if (result.find("Unknown ACL permission",0,len(result)) == -1): + self.fail(result) + def test_illegal_extension_lines(self): """ Test illegal extension lines """ - + aclf = self.get_acl_file() aclf.write('group admins bob@QPID \n') aclf.write(' \ \n') aclf.write('joe@QPID \n') aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() - if (result.text.find("contains an illegal extension",0,len(result.text)) == -1): + aclf.close() + + result = self.reload_acl() + if (result.find("contains an illegal extension",0,len(result)) == -1): self.fail(result) - if (result.text.find("Non-continuation line must start with \"group\" or \"acl\"",0,len(result.text)) == -1): + if (result.find("Non-continuation line must start with \"group\" or \"acl\"",0,len(result)) == -1): self.fail(result) - def test_llegal_extension_lines(self): + def test_illegal_extension_lines(self): """ Test proper extention lines """ @@ -190,9 +280,9 @@ class ACLTests(TestBase010): aclf.write('host/123.example.com@TEST.COM\n') # should be allowed aclf.write('acl allow all all') aclf.close() - + result = self.reload_acl() - if (result.text.find("ACL format error",0,len(result.text)) != -1): + if (result): self.fail(result) def test_user_realm(self): @@ -205,9 +295,9 @@ class ACLTests(TestBase010): aclf.write('acl deny admin bind exchange\n') aclf.write('acl allow all all') aclf.close() - + result = self.reload_acl() - if (result.text.find("Username 'bob' must contain a realm",0,len(result.text)) == -1): + if (result.find("Username 'bob' must contain a realm",0,len(result)) == -1): self.fail(result) def test_allowed_chars_for_username(self): @@ -221,18 +311,18 @@ class ACLTests(TestBase010): aclf.write('group test4 host/somemachine.example.com@EXAMPLE.COM\n') # should be allowed aclf.write('acl allow all all') aclf.close() - + result = self.reload_acl() - if (result.text.find("ACL format error",0,len(result.text)) != -1): + if (result): self.fail(result) aclf = self.get_acl_file() aclf.write('group test1 joe$H@EXAMPLE.com\n') # shouldn't be allowed aclf.write('acl allow all all') - aclf.close() + aclf.close() result = self.reload_acl() - if (result.text.find("Username \"joe$H@EXAMPLE.com\" contains illegal characters",0,len(result.text)) == -1): + if (result.find("Username \"joe$H@EXAMPLE.com\" contains illegal characters",0,len(result)) == -1): self.fail(result) #===================================== @@ -243,118 +333,238 @@ class ACLTests(TestBase010): """ Test illegal queue policy """ - + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ding\n') aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() + aclf.close() + + result = self.reload_acl() expected = "ding is not a valid value for 'policytype', possible values are one of" \ - " { 'ring' 'ring_strict' 'flow_to_disk' 'reject' }"; - if (result.text != expected): - self.fail(result) + " { 'ring' 'ring_strict' 'flow_to_disk' 'reject' }"; + if (result.find(expected) == -1): + self.fail(result) - def test_illegal_queue_size(self): + def test_illegal_queuemaxsize_upper_limit_spec(self): """ Test illegal queue policy """ - + # + # Use maxqueuesize + # aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=-1\n') aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() - expected = "-1 is not a valid value for 'maxqueuesize', " \ - "values should be between 0 and 9223372036854775807"; - if (result.text != expected): - self.fail(result) + aclf.close() + + result = self.reload_acl() + expected = "-1 is not a valid value for 'queuemaxsizeupperlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=9223372036854775808\n') - aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() - expected = "9223372036854775808 is not a valid value for 'maxqueuesize', " \ + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "9223372036854775808 is not a valid value for 'queuemaxsizeupperlimit', " \ "values should be between 0 and 9223372036854775807"; - if (result.text != expected): - self.fail(result) + if (result.find(expected) == -1): + self.fail(result) + + # + # Use queuemaxsizeupperlimit + # + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizeupperlimit=-1\n') + aclf.write('acl allow all all') + aclf.close() + result = self.reload_acl() + expected = "-1 is not a valid value for 'queuemaxsizeupperlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) - def test_illegal_queue_count(self): + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizeupperlimit=9223372036854775808\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "9223372036854775808 is not a valid value for 'queuemaxsizeupperlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) + + + + def test_illegal_queuemaxcount_upper_limit_spec(self): """ Test illegal queue policy """ - + # + # Use maxqueuecount + # + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=-1\n') aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() - expected = "-1 is not a valid value for 'maxqueuecount', " \ - "values should be between 0 and 9223372036854775807"; - if (result.text != expected): - self.fail(result) + aclf.close() + + result = self.reload_acl() + expected = "-1 is not a valid value for 'queuemaxcountupperlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=9223372036854775808\n') - aclf.write('acl allow all all') - aclf.close() - - result = self.reload_acl() - expected = "9223372036854775808 is not a valid value for 'maxqueuecount', " \ + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "9223372036854775808 is not a valid value for 'queuemaxcountupperlimit', " \ "values should be between 0 and 9223372036854775807"; - if (result.text != expected): - self.fail(result) + if (result.find(expected) == -1): + self.fail(result) + + # + # use maxqueuecountupperlimit + # + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountupperlimit=-1\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "-1 is not a valid value for 'queuemaxcountupperlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) + + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountupperlimit=9223372036854775808\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "9223372036854775808 is not a valid value for 'queuemaxcountupperlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) + + + def test_illegal_queuemaxsize_lower_limit_spec(self): + """ + Test illegal queue policy + """ + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizelowerlimit=-1\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "-1 is not a valid value for 'queuemaxsizelowerlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) + + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizelowerlimit=9223372036854775808\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "9223372036854775808 is not a valid value for 'queuemaxsizelowerlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) + + + + def test_illegal_queuemaxcount_lower_limit_spec(self): + """ + Test illegal queue policy + """ + + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountlowerlimit=-1\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "-1 is not a valid value for 'queuemaxcountlowerlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) + + aclf = self.get_acl_file() + aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountlowerlimit=9223372036854775808\n') + aclf.write('acl allow all all') + aclf.close() + + result = self.reload_acl() + expected = "9223372036854775808 is not a valid value for 'queuemaxcountlowerlimit', " \ + "values should be between 0 and 9223372036854775807"; + if (result.find(expected) == -1): + self.fail(result) #===================================== # ACL queue tests #===================================== - + def test_queue_allow_mode(self): """ Test cases for queue acl in allow mode """ aclf = self.get_acl_file() - aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n') + aclf.write('acl deny bob@QPID access queue name=q1\n') + aclf.write('acl deny bob@QPID create queue name=q1 durable=true\n') aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n') aclf.write('acl deny bob@QPID access queue name=q3\n') aclf.write('acl deny bob@QPID purge queue name=q3\n') - aclf.write('acl deny bob@QPID delete queue name=q4\n') - aclf.write('acl deny bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n') + aclf.write('acl deny bob@QPID delete queue name=q4\n') + aclf.write('acl deny bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n') aclf.write('acl allow all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') - + + try: + session.queue_declare(queue="q1", durable=True) + self.fail("ACL should deny queue create request with name=q1 durable=true"); + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) + session = self.get_session('bob','bob') + try: session.queue_declare(queue="q1", durable=True, passive=True) - self.fail("ACL should deny queue create request with name=q1 durable=true passive=true"); + self.fail("ACL should deny queue passive declare request with name=q1 durable=true"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: queue_options = {} - queue_options["qpid.policy_type"] = "ring" + queue_options["qpid.policy_type"] = "ring" session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) self.fail("ACL should deny queue create request with name=q2 exclusive=true qpid.policy_type=ring"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: queue_options = {} - queue_options["qpid.policy_type"] = "ring_strict" - session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) + queue_options["qpid.policy_type"] = "ring_strict" + session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow queue create request with name=q2 exclusive=true qpid.policy_type=ring_strict"); @@ -362,17 +572,17 @@ class ACLTests(TestBase010): try: queue_options = {} queue_options["qpid.max_count"] = 200 - queue_options["qpid.max_size"] = 500 + queue_options["qpid.max_size"] = 500 session.queue_declare(queue="q5", exclusive=True, arguments=queue_options) self.fail("ACL should deny queue create request with name=q2, qpid.max_size=500 and qpid.max_count=200"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') try: queue_options = {} queue_options["qpid.max_count"] = 200 - queue_options["qpid.max_size"] = 100 + queue_options["qpid.max_size"] = 100 session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): @@ -390,63 +600,71 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.queue_purge(queue="q3") self.fail("ACL should deny queue purge request for q3"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.queue_purge(queue="q4") except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow queue purge request for q4"); - + try: session.queue_delete(queue="q4") self.fail("ACL should deny queue delete request for q4"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.queue_delete(queue="q3") except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow queue delete request for q3"); - + def test_queue_deny_mode(self): """ Test cases for queue acl in deny mode """ aclf = self.get_acl_file() - aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n') + aclf.write('acl allow bob@QPID access queue name=q1\n') + aclf.write('acl allow bob@QPID create queue name=q1 durable=true\n') aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n') aclf.write('acl allow bob@QPID access queue name=q3\n') aclf.write('acl allow bob@QPID purge queue name=q3\n') - aclf.write('acl allow bob@QPID create queue name=q3\n') - aclf.write('acl allow bob@QPID create queue name=q4\n') - aclf.write('acl allow bob@QPID delete queue name=q4\n') - aclf.write('acl allow bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n') + aclf.write('acl allow bob@QPID create queue name=q3\n') + aclf.write('acl allow bob@QPID create queue name=q4\n') + aclf.write('acl allow bob@QPID delete queue name=q4\n') + aclf.write('acl allow bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n') + aclf.write('acl allow bob@QPID create queue name=q6 queuemaxsizelowerlimit=50 queuemaxsizeupperlimit=100 queuemaxcountlowerlimit=50 queuemaxcountupperlimit=100\n') aclf.write('acl allow anonymous all all\n') aclf.write('acl deny all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') - + + try: + session.queue_declare(queue="q1", durable=True) + except qpid.session.SessionException, e: + if (403 == e.args[0].error_code): + self.fail("ACL should allow queue create request with name=q1 durable=true"); + try: session.queue_declare(queue="q1", durable=True, passive=True) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow queue create request with name=q1 durable=true passive=true"); - + self.fail("ACL should allow queue passive declare request with name=q1 durable=true passive=true"); + try: session.queue_declare(queue="q1", durable=False, passive=False) self.fail("ACL should deny queue create request with name=q1 durable=true passive=false"); @@ -458,32 +676,81 @@ class ACLTests(TestBase010): session.queue_declare(queue="q2", exclusive=False) self.fail("ACL should deny queue create request with name=q2 exclusive=false"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: queue_options = {} queue_options["qpid.max_count"] = 200 - queue_options["qpid.max_size"] = 500 + queue_options["qpid.max_size"] = 500 session.queue_declare(queue="q5", arguments=queue_options) - self.fail("ACL should deny queue create request with name=q2 maxqueuesize=500 maxqueuecount=200"); + self.fail("ACL should deny queue create request with name=q5 maxqueuesize=500 maxqueuecount=200"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') try: queue_options = {} queue_options["qpid.max_count"] = 100 - queue_options["qpid.max_size"] = 500 - session.queue_declare(queue="q5", arguments=queue_options) + queue_options["qpid.max_size"] = 500 + session.queue_declare(queue="q5", arguments=queue_options) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow queue create request with name=q2 maxqueuesize=500 maxqueuecount=200"); + self.fail("ACL should allow queue create request with name=q5 maxqueuesize=500 maxqueuecount=200"); + + try: + queue_options = {} + queue_options["qpid.max_count"] = 49 + queue_options["qpid.max_size"] = 100 + session.queue_declare(queue="q6", arguments=queue_options) + self.fail("ACL should deny queue create request with name=q6 maxqueuesize=100 maxqueuecount=49"); + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + queue_options = {} + queue_options["qpid.max_count"] = 101 + queue_options["qpid.max_size"] = 100 + session.queue_declare(queue="q6", arguments=queue_options) + self.fail("ACL should allow queue create request with name=q6 maxqueuesize=100 maxqueuecount=101"); + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + queue_options = {} + queue_options["qpid.max_count"] = 100 + queue_options["qpid.max_size"] = 49 + session.queue_declare(queue="q6", arguments=queue_options) + self.fail("ACL should deny queue create request with name=q6 maxqueuesize=49 maxqueuecount=100"); + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + queue_options = {} + queue_options["qpid.max_count"] = 100 + queue_options["qpid.max_size"] =101 + session.queue_declare(queue="q6", arguments=queue_options) + self.fail("ACL should deny queue create request with name=q6 maxqueuesize=101 maxqueuecount=100"); + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: + queue_options = {} + queue_options["qpid.max_count"] = 50 + queue_options["qpid.max_size"] = 50 + session.queue_declare(queue="q6", arguments=queue_options) + except qpid.session.SessionException, e: + if (403 == e.args[0].error_code): + self.fail("ACL should allow queue create request with name=q6 maxqueuesize=50 maxqueuecount=50"); try: queue_options = {} queue_options["qpid.policy_type"] = "ring" - session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) + session.queue_declare(queue="q2", exclusive=True, arguments=queue_options) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow queue create request for q2 with exclusive=true policytype=ring"); @@ -501,14 +768,14 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.queue_purge(queue="q4") self.fail("ACL should deny queue purge request for q4"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.queue_purge(queue="q3") except qpid.session.SessionException, e: @@ -520,14 +787,14 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow queue query request for q3"); - + try: session.queue_delete(queue="q3") self.fail("ACL should deny queue delete request for q3"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.queue_delete(queue="q4") except qpid.session.SessionException, e: @@ -537,54 +804,62 @@ class ACLTests(TestBase010): #===================================== # ACL exchange tests #===================================== - + def test_exchange_acl_allow_mode(self): - session = self.get_session('bob','bob') + session = self.get_session('bob','bob') session.queue_declare(queue="baz") """ Test cases for exchange acl in allow mode """ aclf = self.get_acl_file() - aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n') + aclf.write('acl deny bob@QPID access exchange name=testEx\n') + aclf.write('acl deny bob@QPID create exchange name=testEx durable=true\n') aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n') aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n') aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n') aclf.write('acl deny bob@QPID delete exchange name=myEx\n') aclf.write('acl allow all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') session.queue_declare(queue='q1') session.queue_declare(queue='q2') session.exchange_declare(exchange='myEx', type='direct') try: + session.exchange_declare(exchange='testEx', durable=True) + self.fail("ACL should deny exchange create request with name=testEx durable=true"); + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) + session = self.get_session('bob','bob') + + try: session.exchange_declare(exchange='testEx', durable=True, passive=True) - self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true"); + self.fail("ACL should deny passive exchange declare request with name=testEx durable=true passive=true"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: - session.exchange_declare(exchange='testEx', type='direct', durable=True, passive=False) + session.exchange_declare(exchange='testEx', type='direct', durable=False) except qpid.session.SessionException, e: print e if (403 == e.args[0].error_code): - self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true and passive=true"); - + self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true"); + try: session.exchange_declare(exchange='ex1', type='direct') self.fail("ACL should deny exchange create request with name=ex1 type=direct"); - except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + except qpid.session.SessionException, e: + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.exchange_declare(exchange='myXml', type='direct') except qpid.session.SessionException, e: @@ -606,13 +881,13 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.exchange_query(name='amq.topic') + session.exchange_query(name='amq.topic') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange query request for exchange='amq.topic'"); - + try: - session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*') + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk2.*'"); @@ -621,12 +896,12 @@ class ACLTests(TestBase010): session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1') self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') try: session.exchange_bind(exchange='myEx', queue='q1', binding_key='x') - except qpid.session.SessionException, e: + except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q1', binding_key='x'"); @@ -640,7 +915,7 @@ class ACLTests(TestBase010): session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1') self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'"); except qpid.session.SessionException, e: - self.assertEqual(403,e.args[0].error_code) + self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') try: @@ -654,20 +929,20 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q2', binding_key='rk1'"); - + try: session.exchange_delete(exchange='myEx') self.fail("ACL should deny exchange delete request for myEx"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.exchange_delete(exchange='myXml') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange delete request for myXml"); - + def test_exchange_acl_deny_mode(self): session = self.get_session('bob','bob') @@ -677,19 +952,19 @@ class ACLTests(TestBase010): Test cases for exchange acl in deny mode """ aclf = self.get_acl_file() - aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n') - aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n') + aclf.write('acl allow bob@QPID create exchange name=myEx durable=true\n') + aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n') aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n') aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') aclf.write('acl allow bob@QPID delete exchange name=myEx\n') - aclf.write('acl allow anonymous all all\n') + aclf.write('acl allow anonymous all all\n') aclf.write('acl deny all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') try: @@ -697,14 +972,14 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange create request for myEx with durable=true and passive=false"); - + try: session.exchange_declare(exchange='myEx', type='direct', durable=False) self.fail("ACL should deny exchange create request with name=myEx durable=false"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='foo.bar') except qpid.session.SessionException, e: @@ -759,13 +1034,13 @@ class ACLTests(TestBase010): session = self.get_session('bob','bob') try: - session.exchange_query(name='myEx') + session.exchange_query(name='myEx') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange query request for exchange='myEx'"); - + try: - session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*') + session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk1.*'"); @@ -776,7 +1051,7 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.exchange_delete(exchange='myEx') except qpid.session.SessionException, e: @@ -805,7 +1080,7 @@ class ACLTests(TestBase010): aclf.close() result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): + if (result): self.fail(result) bob = BrokerAdmin(self.config.broker, "bob", "bob") @@ -832,24 +1107,24 @@ class ACLTests(TestBase010): #===================================== # ACL consume tests #===================================== - + def test_consume_allow_mode(self): """ Test cases for consume in allow mode """ aclf = self.get_acl_file() aclf.write('acl deny bob@QPID consume queue name=q1\n') - aclf.write('acl deny bob@QPID consume queue name=q2\n') + aclf.write('acl deny bob@QPID consume queue name=q2\n') aclf.write('acl allow all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') - - + + try: session.queue_declare(queue='q1') session.queue_declare(queue='q2') @@ -857,27 +1132,27 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow create queue request"); - + try: session.message_subscribe(queue='q1', destination='myq1') self.fail("ACL should deny subscription for queue='q1'"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.message_subscribe(queue='q2', destination='myq1') self.fail("ACL should deny subscription for queue='q2'"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.message_subscribe(queue='q3', destination='myq1') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow subscription for q3"); - + self.fail("ACL should allow subscription for q3"); + def test_consume_deny_mode(self): """ @@ -886,18 +1161,18 @@ class ACLTests(TestBase010): aclf = self.get_acl_file() aclf.write('acl allow bob@QPID consume queue name=q1\n') aclf.write('acl allow bob@QPID consume queue name=q2\n') - aclf.write('acl allow bob@QPID create queue\n') - aclf.write('acl allow anonymous all\n') + aclf.write('acl allow bob@QPID create queue\n') + aclf.write('acl allow anonymous all\n') aclf.write('acl deny all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') - - + + try: session.queue_declare(queue='q1') session.queue_declare(queue='q2') @@ -911,20 +1186,20 @@ class ACLTests(TestBase010): session.message_subscribe(queue='q2', destination='myq2') except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow subscription for q1 and q2"); - + self.fail("ACL should allow subscription for q1 and q2"); + try: session.message_subscribe(queue='q3', destination='myq3') self.fail("ACL should deny subscription for queue='q3'"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + #===================================== # ACL publish tests #===================================== - + def test_publish_acl_allow_mode(self): """ Test various publish acl @@ -932,40 +1207,40 @@ class ACLTests(TestBase010): aclf = self.get_acl_file() aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n') aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n') - aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n') + aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n') aclf.write('acl allow all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') - + props = session.delivery_properties(routing_key="rk1") - - try: + + try: session.message_transfer(destination="amq.direct", message=Message(props,"Test")) self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk1"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) - session = self.get_session('bob','bob') - + session = self.get_session('bob','bob') + try: session.message_transfer(destination="amq.topic", message=Message(props,"Test")) self.fail("ACL should deny message transfer to name=amq.topic"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) session = self.get_session('bob','bob') - + try: session.exchange_declare(exchange='myEx', type='direct', durable=False) session.message_transfer(destination="myEx", message=Message(props,"Test")) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow message transfer to exchange myEx with routing key rk1"); - - + self.fail("ACL should allow message transfer to exchange myEx with routing key rk1"); + + props = session.delivery_properties(routing_key="rk2") try: session.message_transfer(destination="amq.direct", message=Message(props,"Test")) @@ -982,39 +1257,39 @@ class ACLTests(TestBase010): aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n') aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n') aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n') - aclf.write('acl allow bob@QPID create exchange\n') - aclf.write('acl allow anonymous all all \n') + aclf.write('acl allow bob@QPID create exchange\n') + aclf.write('acl allow anonymous all all \n') aclf.write('acl deny all all') - aclf.close() - + aclf.close() + result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): - self.fail(result) - + if (result): + self.fail(result) + session = self.get_session('bob','bob') - + props = session.delivery_properties(routing_key="rk2") - - try: + + try: session.message_transfer(destination="amq.direct", message=Message(props,"Test")) self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk2"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) - session = self.get_session('bob','bob') - + session = self.get_session('bob','bob') + try: session.message_transfer(destination="amq.topic", message=Message(props,"Test")) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow message transfer to exchange amq.topic with any routing key"); - + self.fail("ACL should allow message transfer to exchange amq.topic with any routing key"); + try: session.exchange_declare(exchange='myEx', type='direct', durable=False) session.message_transfer(destination="myEx", message=Message(props,"Test")) except qpid.session.SessionException, e: if (403 == e.args[0].error_code): - self.fail("ACL should allow message transfer to exchange myEx with routing key=rk2"); - + self.fail("ACL should allow message transfer to exchange myEx with routing key=rk2"); + props = session.delivery_properties(routing_key="rk1") try: @@ -1022,7 +1297,7 @@ class ACLTests(TestBase010): self.fail("ACL should deny message transfer to name=myEx routingkey=rk1"); except qpid.session.SessionException, e: self.assertEqual(403,e.args[0].error_code) - session = self.get_session('bob','bob') + session = self.get_session('bob','bob') try: session.message_transfer(destination="amq.direct", message=Message(props,"Test")) @@ -1056,7 +1331,7 @@ class ACLTests(TestBase010): aclf.close() result = self.reload_acl() - if (result.text.find("format error",0,len(result.text)) != -1): + if (result): self.fail(result) ts = None @@ -1088,6 +1363,200 @@ class ACLTests(TestBase010): admin.set_timestamp_cfg(ts) #should pass + + #===================================== + # QMF Functional tests + #===================================== + + def test_qmf_functional_tests(self): + """ + Test using QMF method hooks into ACL logic + """ + aclf = self.get_acl_file() + aclf.write('group admins moe@COMPANY.COM \\\n') + aclf.write(' larry@COMPANY.COM \\\n') + aclf.write(' curly@COMPANY.COM \\\n') + aclf.write(' shemp@COMPANY.COM\n') + aclf.write('group auditors aaudit@COMPANY.COM baudit@COMPANY.COM caudit@COMPANY.COM \\\n') + aclf.write(' daudit@COMPANY.COM eaduit@COMPANY.COM eaudit@COMPANY.COM\n') + aclf.write('group tatunghosts tatung01@COMPANY.COM \\\n') + aclf.write(' tatung02/x86.build.company.com@COMPANY.COM \\\n') + aclf.write(' tatung03/x86.build.company.com@COMPANY.COM \\\n') + aclf.write(' tatung04/x86.build.company.com@COMPANY.COM \n') + aclf.write('group publishusers publish@COMPANY.COM x-pubs@COMPANY.COM\n') + aclf.write('acl allow-log admins all all\n') + aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n') + aclf.write('acl allow-log anonymous create queue\n') + aclf.write('acl allow-log anonymous all exchange name=qmf.*\n') + aclf.write('acl allow-log anonymous all exchange name=amq.direct\n') + aclf.write('acl allow-log anonymous all exchange name=qpid.management\n') + aclf.write('acl allow-log anonymous access method name=*\n') + aclf.write('# end hack alert\n') + aclf.write('acl allow-log auditors all exchange name=company.topic routingkey=private.audit.*\n') + aclf.write('acl allow-log tatunghosts publish exchange name=company.topic routingkey=tatung.*\n') + aclf.write('acl allow-log tatunghosts publish exchange name=company.direct routingkey=tatung-service-queue\n') + aclf.write('acl allow-log publishusers create queue\n') + aclf.write('acl allow-log publishusers publish exchange name=qpid.management routingkey=broker\n') + aclf.write('acl allow-log publishusers publish exchange name=qmf.default.topic routingkey=*\n') + aclf.write('acl allow-log publishusers publish exchange name=qmf.default.direct routingkey=*\n') + aclf.write('acl allow-log all bind exchange name=company.topic routingkey=tatung.*\n') + aclf.write('acl allow-log all bind exchange name=company.direct routingkey=tatung-service-queue\n') + aclf.write('acl allow-log all consume queue\n') + aclf.write('acl allow-log all access exchange\n') + aclf.write('acl allow-log all access queue\n') + aclf.write('acl allow-log all create queue name=tmp.* durable=false autodelete=true exclusive=true policytype=ring\n') + aclf.write('acl allow mrQ create queue queuemaxsizelowerlimit=100 queuemaxsizeupperlimit=200 queuemaxcountlowerlimit=300 queuemaxcountupperlimit=400\n') + aclf.write('acl deny-log all all\n') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + # + # define some group lists + # + g_admins = ['moe@COMPANY.COM', \ + 'larry@COMPANY.COM', \ + 'curly@COMPANY.COM', \ + 'shemp@COMPANY.COM'] + + g_auditors = [ 'aaudit@COMPANY.COM','baudit@COMPANY.COM','caudit@COMPANY.COM', \ + 'daudit@COMPANY.COM','eaduit@COMPANY.COM','eaudit@COMPANY.COM'] + + g_tatunghosts = ['tatung01@COMPANY.COM', \ + 'tatung02/x86.build.company.com@COMPANY.COM', \ + 'tatung03/x86.build.company.com@COMPANY.COM', \ + 'tatung04/x86.build.company.com@COMPANY.COM'] + + g_publishusers = ['publish@COMPANY.COM', 'x-pubs@COMPANY.COM'] + + g_public = ['jpublic@COMPANY.COM', 'me@yahoo.com'] + + g_all = g_admins + g_auditors + g_tatunghosts + g_publishusers + g_public + + action_all = ['consume','publish','create','access','bind','unbind','delete','purge','update'] + + # + # Run some tests verifying against users who are in and who are out of given groups. + # + + for u in g_admins: + self.Lookup(u, "create", "queue", "anything", {"durable":"true"}, "allow-log") + + uInTest = g_auditors + g_admins + uOutTest = self.AllBut(g_all, uInTest) + + for u in uInTest: + self.LookupPublish(u, "company.topic", "private.audit.This", "allow-log") + + for u in uInTest: + for a in action_all: + self.Lookup(u, a, "exchange", "company.topic", {"routingkey":"private.audit.This"}, "allow-log") + + for u in uOutTest: + self.LookupPublish(u, "company.topic", "private.audit.This", "deny-log") + self.Lookup(u, "bind", "exchange", "company.topic", {"routingkey":"private.audit.This"}, "deny-log") + + uInTest = g_admins + g_tatunghosts + uOutTest = self.AllBut(g_all, uInTest) + + for u in uInTest: + self.LookupPublish(u, "company.topic", "tatung.this2", "allow-log") + self.LookupPublish(u, "company.direct", "tatung-service-queue", "allow-log") + + for u in uOutTest: + self.LookupPublish(u, "company.topic", "tatung.this2", "deny-log") + self.LookupPublish(u, "company.direct", "tatung-service-queue", "deny-log") + + for u in uOutTest: + for a in ["bind", "access"]: + self.Lookup(u, a, "exchange", "company.topic", {"routingkey":"tatung.this2"}, "allow-log") + self.Lookup(u, a, "exchange", "company.direct", {"routingkey":"tatung-service-queue"}, "allow-log") + + uInTest = g_admins + g_publishusers + uOutTest = self.AllBut(g_all, uInTest) + + for u in uInTest: + self.LookupPublish(u, "qpid.management", "broker", "allow-log") + self.LookupPublish(u, "qmf.default.topic", "this3", "allow-log") + self.LookupPublish(u, "qmf.default.direct", "this4", "allow-log") + + for u in uOutTest: + self.LookupPublish(u, "qpid.management", "broker", "deny-log") + self.LookupPublish(u, "qmf.default.topic", "this3", "deny-log") + self.LookupPublish(u, "qmf.default.direct", "this4", "deny-log") + + for u in uOutTest: + for a in ["bind"]: + self.Lookup(u, a, "exchange", "qpid.management", {"routingkey":"broker"}, "deny-log") + self.Lookup(u, a, "exchange", "qmf.default.topic", {"routingkey":"this3"}, "deny-log") + self.Lookup(u, a, "exchange", "qmf.default.direct", {"routingkey":"this4"}, "deny-log") + for a in ["access"]: + self.Lookup(u, a, "exchange", "qpid.management", {"routingkey":"broker"}, "allow-log") + self.Lookup(u, a, "exchange", "qmf.default.topic", {"routingkey":"this3"}, "allow-log") + self.Lookup(u, a, "exchange", "qmf.default.direct", {"routingkey":"this4"}, "allow-log") + + # Test against queue size limits + + self.Lookup('mrQ', 'create', 'queue', 'abc', {"maxqueuesize":"150", "maxqueuecount":"350"}, "allow") + self.Lookup('mrQ', 'create', 'queue', 'def', {"maxqueuesize":"99", "maxqueuecount":"350"}, "deny") + self.Lookup('mrQ', 'create', 'queue', 'uvw', {"maxqueuesize":"201", "maxqueuecount":"350"}, "deny") + self.Lookup('mrQ', 'create', 'queue', 'xyz', {"maxqueuesize":"150", "maxqueuecount":"299"}, "deny") + self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"150", "maxqueuecount":"401"}, "deny") + self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"0", "maxqueuecount":"401"}, "deny") + self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"150", "maxqueuecount":"0" }, "deny") + + + #===================================== + # Connection limits + #===================================== + + def test_connection_limits(self): + """ + Test ACL control connection limits + """ + # By username should be able to connect twice per user + try: + sessiona1 = self.get_session_by_port('alice','alice', self.port_u()) + sessiona2 = self.get_session_by_port('alice','alice', self.port_u()) + except Exception, e: + self.fail("Could not create two connections for user alice: " + str(e)) + + # Third session should fail + try: + sessiona3 = self.get_session_by_port('alice','alice', self.port_u()) + self.fail("Should not be able to create third connection for user alice") + except Exception, e: + result = None + + try: + sessionb1 = self.get_session_by_port('bob','bob', self.port_u()) + sessionb2 = self.get_session_by_port('bob','bob', self.port_u()) + except Exception, e: + self.fail("Could not create two connections for user bob: " + str(e)) + + try: + sessionb3 = self.get_session_by_port('bob','bob', self.port_u()) + self.fail("Should not be able to create third connection for user bob") + except Exception, e: + result = None + + # By IP address should be able to connect twice per client address + try: + sessionb1 = self.get_session_by_port('alice','alice', self.port_i()) + sessionb2 = self.get_session_by_port('bob','bob', self.port_i()) + except Exception, e: + self.fail("Could not create two connections for client address: " + str(e)) + + # Third session should fail + try: + sessionb3 = self.get_session_by_port('charlie','charlie', self.port_i()) + self.fail("Should not be able to create third connection for client address") + except Exception, e: + result = None + + class BrokerAdmin: def __init__(self, broker, username=None, password=None): self.connection = qpid.messaging.Connection(broker) diff --git a/cpp/src/tests/amqp_0_10/Map.cpp b/cpp/src/tests/amqp_0_10/Map.cpp deleted file mode 100644 index ffb235829e..0000000000 --- a/cpp/src/tests/amqp_0_10/Map.cpp +++ /dev/null @@ -1,98 +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 "amqp_0_10/unit_test.h" -#include "qpid/amqp_0_10/Map.h" -#include "qpid/amqp_0_10/Array.h" -#include "qpid/amqp_0_10/Struct32.h" -#include "qpid/amqp_0_10/UnknownType.h" -#include "qpid/amqp_0_10/Codec.h" -#include <iostream> - -using namespace qpid::amqp_0_10; -using namespace std; - -QPID_AUTO_TEST_SUITE(MapTestSuite) - - QPID_AUTO_TEST_CASE(testGetSet) { - MapValue v; - v = Str8("foo"); - BOOST_CHECK(v.get<Str8>()); - BOOST_CHECK(!v.get<uint8_t>()); - BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo"); - - v = uint8_t(42); - BOOST_CHECK(!v.get<Str8>()); - BOOST_CHECK(v.get<uint8_t>()); - BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42); - - v = uint16_t(12); - BOOST_CHECK(v.get<uint16_t>()); - BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12); -} - -template <class R> struct TestVisitor : public MapValue::Visitor<R> { - template <class T> R operator()(const T&) const { throw MapValue::BadTypeException(); } - R operator()(const R& r) const { return r; } -}; - -QPID_AUTO_TEST_CASE(testVisit) { - MapValue v; - v = Str8("foo"); - BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo"); - v = Uint16(42); - BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42); - try { - v.apply_visitor(TestVisitor<bool>()); - BOOST_FAIL("Expecting exception"); - } - catch(const MapValue::BadTypeException&) {} -} - - -QPID_AUTO_TEST_CASE(testEncodeMapValue) { - MapValue mv; - std::string data; - mv = Str8("hello"); - Codec::encode(back_inserter(data))(mv); - BOOST_CHECK_EQUAL(data.size(), Codec::size(mv)); - MapValue mv2; - Codec::decode(data.begin())(mv2); - BOOST_CHECK_EQUAL(mv2.getCode(), 0x85); - BOOST_REQUIRE(mv2.get<Str8>()); - BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello"); -} - -QPID_AUTO_TEST_CASE(testEncode) { - Map map; - std::string data; - map["A"] = true; - map["b"] = Str8("hello"); - Codec::encode(back_inserter(data))(map); - BOOST_CHECK_EQUAL(Codec::size(map), data.size()); - Map map2; - Codec::decode(data.begin())(map2); - BOOST_CHECK_EQUAL(map.size(), 2u); - BOOST_CHECK(map["A"].get<bool>()); - BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello"); -} - - -QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp deleted file mode 100644 index f54ee0da22..0000000000 --- a/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp +++ /dev/null @@ -1,49 +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 "amqp_0_10/unit_test.h" -#include "qpid/amqp_0_10/ProxyTemplate.h" -#include <boost/any.hpp> - -QPID_AUTO_TEST_SUITE(ProxyTemplateTestSuite) - -using namespace qpid::amqp_0_10; - -struct ToAny { - template <class T> - boost::any operator()(const T& t) { return boost::any(t); } -}; - -struct AnyProxy : public ProxyTemplate<ToAny, boost::any> {}; - -QPID_AUTO_TEST_CASE(testAnyProxy) { - AnyProxy p; - boost::any a=p.connectionTune(1,2,3,4); - BOOST_CHECK_EQUAL(a.type().name(), typeid(connection::Tune).name()); - connection::Tune* tune=boost::any_cast<connection::Tune>(&a); - BOOST_REQUIRE(tune); - BOOST_CHECK_EQUAL(tune->channelMax, 1u); - BOOST_CHECK_EQUAL(tune->maxFrameSize, 2u); - BOOST_CHECK_EQUAL(tune->heartbeatMin, 3u); - BOOST_CHECK_EQUAL(tune->heartbeatMax, 4u); -} - -QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/amqp_0_10/apply.cpp b/cpp/src/tests/amqp_0_10/apply.cpp deleted file mode 100644 index 0aa4421791..0000000000 --- a/cpp/src/tests/amqp_0_10/apply.cpp +++ /dev/null @@ -1,99 +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 "amqp_0_10/unit_test.h" -#include "qpid/amqp_0_10/specification.h" -#include "qpid/amqp_0_10/ApplyControl.h" - -QPID_AUTO_TEST_SUITE(VisitorTestSuite) - -using namespace qpid::amqp_0_10; - -struct GetCode : public ApplyFunctor<uint8_t> { - template <class T> uint8_t operator()(const T&) const { return T::CODE; } -}; - -struct SetChannelMax : ApplyFunctor<void> { - template <class T> void operator()(T&) const { BOOST_FAIL(""); } - void operator()(connection::Tune& t) const { t.channelMax=42; } -}; - -struct TestFunctor { - typedef bool result_type; - bool operator()(const connection::Tune& tune) { - BOOST_CHECK_EQUAL(tune.channelMax, 1u); - BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); - BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); - BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); - return true; - } - template <class T> - bool operator()(const T&) { return false; } -}; - -QPID_AUTO_TEST_CASE(testApply) { - connection::Tune tune(1,2,3,4); - Control* p = &tune; - - // boost oddity - without the cast we get undefined symbol errors. - BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE); - - TestFunctor tf; - BOOST_CHECK(apply(tf, *p)); - - connection::Start start; - p = &start; - BOOST_CHECK(!apply(tf, *p)); - - apply(SetChannelMax(), tune); - BOOST_CHECK_EQUAL(tune.channelMax, 42); -} - -struct VoidTestFunctor { - typedef void result_type; - - int code; - VoidTestFunctor() : code() {} - - void operator()(const connection::Tune& tune) { - BOOST_CHECK_EQUAL(tune.channelMax, 1u); - BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); - BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); - BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); - code=connection::Tune::CODE; - } - template <class T> - void operator()(const T&) { code=0xFF; } -}; - -QPID_AUTO_TEST_CASE(testApplyVoid) { - connection::Tune tune(1,2,3,4); - Control* p = &tune; - VoidTestFunctor tf; - apply(tf, *p); - BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code); - - connection::Start start; - p = &start; - apply(tf, *p); - BOOST_CHECK_EQUAL(0xFF, tf.code); -} - -QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/amqp_0_10/handlers.cpp b/cpp/src/tests/amqp_0_10/handlers.cpp deleted file mode 100644 index 91bb304a17..0000000000 --- a/cpp/src/tests/amqp_0_10/handlers.cpp +++ /dev/null @@ -1,125 +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 "amqp_0_10/unit_test.h" -#include "qpid/Exception.h" -#include "qpid/amqp_0_10/Unit.h" -#include "qpid/amqp_0_10/ControlHolder.h" -#include "qpid/amqp_0_10/CommandHolder.h" -#include "qpid/amqp_0_10/handlers.h" -#include "qpid/amqp_0_10/specification.h" - -QPID_AUTO_TEST_SUITE(handler_tests) - -using namespace qpid::amqp_0_10; -using namespace std; - -string called; // Set by called handler function - -// Note on handlers: -// -// Control and Command handlers are separate, both behave the same way, -// so substitute "control or command" for command in the following. -// -// Command handlers derive from CommandHandler and implement functions -// for all the commands they handle. Handling an unimplemented command -// will raise NotImplementedException. -// -// Using virtual inheritance from CommandHandler allows multiple -// handlers to be aggregated into one with multiple inheritance, -// See test code for example. -// -// E.g. the existing broker model would have two control handlers: -// - ConnectionHandler: ControlHandler for connection controls. -// - SessionHandler: ControlHandler for session controls. -// It would have class-command handlers for each AMQP class: -// - QueueHandler, MessageHandler etc.. handle each class. -// And an aggregate handler in place of BrokerAdapter -// - BrokerCommandHandler: public QueueHandler, MessageHandler ... -// -// In other applications (e.g. cluster) any combination of commands -// can be handled by a given handler. It _might_ simplify the code -// to collaps ConnectionHandler and SessionHandler into a single -// ControlHandler (or it might not.) - -struct TestExecutionHandler : public virtual CommandHandler { - void executionSync() { called = "executionSync"; } - // ... etc. for all execution commands -}; - -struct TestMessageHandler : public virtual CommandHandler { - void messageCancel(const Str8&) { called="messageCancel"; } - // ... etc. -}; - -// Aggregate handler for all recognised commands. -struct TestCommandHandler : - public TestExecutionHandler, - public TestMessageHandler - // ... etc. handlers for all command classes. -{}; // Nothing to do. - - -// Sample unit handler, written as a static_visitor. -// Note it could equally be written with if/else statements -// in handle. -// -struct TestUnitHandler : public boost::static_visitor<void> { - TestCommandHandler handler; - void handle(const Unit& u) { u.applyVisitor(*this); } - - void operator()(const Body&) { called="Body"; } - void operator()(const Header&) { called="Header"; } - void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); } - void operator()(const CommandHolder& c) { c.invoke(handler); } -}; - -QPID_AUTO_TEST_CASE(testHandlers) { - TestUnitHandler handler; - Unit u; - - u = Body(); - handler.handle(u); - BOOST_CHECK_EQUAL("Body", called); - - u = Header(); - handler.handle(u); - BOOST_CHECK_EQUAL("Header", called); - - // in_place<Foo>(...) is equivalent to Foo(...) but - // constructs Foo directly in the holder, avoiding - // a copy. - - u = CommandHolder(in_place<execution::Sync>()); - handler.handle(u); - BOOST_CHECK_EQUAL("executionSync", called); - - u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array())); - try { - handler.handle(u); - } catch (const qpid::Exception&) {} - - u = CommandHolder(in_place<message::Cancel>(Str8())); - handler.handle(u); - BOOST_CHECK_EQUAL("messageCancel", called); -} - -QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/amqp_0_10/serialize.cpp b/cpp/src/tests/amqp_0_10/serialize.cpp deleted file mode 100644 index 975d6206ec..0000000000 --- a/cpp/src/tests/amqp_0_10/serialize.cpp +++ /dev/null @@ -1,429 +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 "amqp_0_10/unit_test.h" -#include "amqp_0_10/allSegmentTypes.h" - -#include "qpid/framing/AMQFrame.h" -#include "qpid/framing/Buffer.h" - -#include "qpid/amqp_0_10/Packer.h" -#include "qpid/amqp_0_10/built_in_types.h" -#include "qpid/amqp_0_10/Codec.h" -#include "qpid/amqp_0_10/specification.h" -#include "qpid/amqp_0_10/ControlHolder.h" -#include "qpid/amqp_0_10/Struct32.h" -#include "qpid/amqp_0_10/FrameHeader.h" -#include "qpid/amqp_0_10/Map.h" -#include "qpid/amqp_0_10/Unit.h" -#include "allSegmentTypes.h" - -#include <boost/test/test_case_template.hpp> -#include <boost/type_traits/is_arithmetic.hpp> -#include <boost/utility/enable_if.hpp> -#include <boost/optional.hpp> -#include <boost/mpl/vector.hpp> -#include <boost/mpl/back_inserter.hpp> -#include <boost/mpl/copy.hpp> -#include <boost/mpl/empty_sequence.hpp> -#include <boost/current_function.hpp> -#include <iterator> -#include <string> -#include <sstream> -#include <iostream> -#include <netinet/in.h> - -// Missing operators needed for tests. -namespace boost { -template <class T, size_t N> -std::ostream& operator<<(std::ostream& out, const array<T,N>& a) { - std::ostream_iterator<T> o(out, " "); - std::copy(a.begin(), a.end(), o); - return out; -} -} // boost - -QPID_AUTO_TEST_SUITE(SerializeTestSuite) - -using namespace std; -namespace mpl=boost::mpl; -using namespace qpid::amqp_0_10; -using qpid::framing::in_place; - -template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typename mpl::back_inserter<A> >::type type; }; -template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; }; -template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; }; - -typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes; -typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes; -typedef mpl::vector<Double, Float>::type FloatTypes; -typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes; -typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes; - -typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes; -typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes; - -// TODO aconway 2008-02-20: should test 64 bit integrals for order also. -QPID_AUTO_TEST_CASE(testNetworkByteOrder) { - string data; - - uint32_t l = 0x11223344; - Codec::encode(std::back_inserter(data))(l); - uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data()); - uint32_t l2 = ntohl(enc); - BOOST_CHECK_EQUAL(l, l2); - - data.clear(); - uint16_t s = 0x1122; - Codec::encode(std::back_inserter(data))(s); - uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data())); - BOOST_CHECK_EQUAL(s, s2); -} - -QPID_AUTO_TEST_CASE(testSetLimit) { - typedef Codec::Encoder<back_insert_iterator<string> > Encoder; - string data; - Encoder encode(back_inserter(data), 3); - encode('1')('2')('3'); - try { - encode('4'); - BOOST_FAIL("Expected exception"); - } catch (...) {} // FIXME aconway 2008-04-03: catch proper exception - BOOST_CHECK_EQUAL(data, "123"); -} - -QPID_AUTO_TEST_CASE(testScopedLimit) { - typedef Codec::Encoder<back_insert_iterator<string> > Encoder; - string data; - Encoder encode(back_inserter(data), 10); - encode(Str8("123")); // 4 bytes - { - Encoder::ScopedLimit l(encode, 3); - encode('a')('b')('c'); - try { - encode('d'); - BOOST_FAIL("Expected exception"); - } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception - } - BOOST_CHECK_EQUAL(data, "\003123abc"); - encode('x')('y')('z'); - try { - encode('!'); - BOOST_FAIL("Expected exception"); - } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception - BOOST_CHECK_EQUAL(data.size(), 10u); -} - -// Assign test values to the various types. -void testValue(bool& b) { b = true; } -void testValue(Bit&) { } -template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; } -void testValue(CharUtf32& c) { c = 43; } -void testValue(long long& l) { l = 0x012345; } -void testValue(Datetime& dt) { dt = qpid::sys::now(); } -void testValue(Uuid& uuid) { uuid=Uuid(true); } -template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; } -void testValue(SequenceNo& s) { s = 42; } -template <size_t N> void testValue(Bin<N>& a) { a.assign(42); } -template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& s) { - char msg[]="foobar"; - s.assign(msg, msg+sizeof(msg)); -} -void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; } -void testValue(Str8& s) { s = "foobar"; } -void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); } - -//typedef mpl::vector<Str8, Str16>::type TestTypes; -/*BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) -{ - string data; - T t; - testValue(t); - Codec::encode(std::back_inserter(data))(t); - - BOOST_CHECK_EQUAL(Codec::size(t), data.size()); - - T t2; - Codec::decode(data.begin())(t2); - BOOST_CHECK_EQUAL(t,t2); -} -*/ - -struct TestMe { - bool encoded, decoded; - char value; - TestMe(char v) : encoded(), decoded(), value(v) {} - template <class S> void encode(S& s) const { - const_cast<TestMe*>(this)->encoded=true; s(value); - } - template <class S> void decode(S& s) { decoded=true; s(value); } - template <class S> void serialize(S& s) { s.split(*this); } -}; - -QPID_AUTO_TEST_CASE(testSplit) { - string data; - TestMe t1('x'); - Codec::encode(std::back_inserter(data))(t1); - BOOST_CHECK(t1.encoded); - BOOST_CHECK(!t1.decoded); - BOOST_CHECK_EQUAL(data, "x"); - - TestMe t2('y'); - Codec::decode(data.begin())(t2); - BOOST_CHECK(!t2.encoded); - BOOST_CHECK(t2.decoded); - BOOST_CHECK_EQUAL(t2.value, 'x'); -} - -QPID_AUTO_TEST_CASE(testControlEncodeDecode) { - string data; - Control::Holder h(in_place<connection::Tune>(1,2,3,4)); - Codec::encode(std::back_inserter(data))(h); - - BOOST_CHECK_EQUAL(data.size(), Codec::size(h)); - - Codec::Decoder<string::iterator> decode(data.begin()); - Control::Holder h2; - decode(h2); - - BOOST_REQUIRE(h2.get()); - BOOST_CHECK_EQUAL(h2.get()->getClassCode(), connection::CODE); - BOOST_CHECK_EQUAL(h2.get()->getCode(), uint8_t(connection::Tune::CODE)); - connection::Tune& tune=static_cast<connection::Tune&>(*h2.get()); - BOOST_CHECK_EQUAL(tune.channelMax, 1u); - BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); - BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); - BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); -} - -QPID_AUTO_TEST_CASE(testStruct32) { - message::DeliveryProperties dp; - dp.priority=message::MEDIUM; - dp.routingKey="foo"; - Struct32 s(dp); - string data; - Codec::encode(back_inserter(data))(s); - - uint32_t structSize; // Starts with size - Codec::decode(data.begin())(structSize); - BOOST_CHECK_EQUAL(structSize, Codec::size(dp) + 2); // +2 for code - BOOST_CHECK_EQUAL(structSize, data.size()-4); // encoded body - - BOOST_CHECK_EQUAL(data.size(), Codec::size(s)); - Struct32 s2; - Codec::decode(data.begin())(s2); - message::DeliveryProperties* dp2 = s2.getIf<message::DeliveryProperties>(); - BOOST_REQUIRE(dp2); - BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); - BOOST_CHECK_EQUAL(dp2->routingKey, "foo"); -} - -QPID_AUTO_TEST_CASE(testStruct32Unknown) { - // Verify we can recode an unknown struct unchanged. - Struct32 s; - string data; - Codec::encode(back_inserter(data))(uint32_t(10)); - data.append(10, 'X'); - Codec::decode(data.begin())(s); - string data2; - Codec::encode(back_inserter(data2))(s); - BOOST_CHECK_EQUAL(data.size(), data2.size()); - BOOST_CHECK_EQUAL(data, data2); -} - -struct DummyPacked { - static const uint8_t PACK=1; - boost::optional<char> i, j; - char k; - Bit l,m; - DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c), l(), m() {} - template <class S> void serialize(S& s) { s(i)(j)(k)(l)(m); } -}; - -Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); } - -QPID_AUTO_TEST_CASE(testPackBits) { - DummyPacked d('a','b','c'); - BOOST_CHECK_EQUAL(packBits(d), 7u); - d.j = boost::none; - BOOST_CHECK_EQUAL(packBits(d), 5u); - d.m = true; - BOOST_CHECK_EQUAL(packBits(d), 0x15u); -} - - -QPID_AUTO_TEST_CASE(testPacked) { - string data; - - Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c'); - BOOST_CHECK_EQUAL(data, "abc"); - data.clear(); - - DummyPacked dummy('a','b','c'); - - Codec::encode(back_inserter(data))(dummy); - BOOST_CHECK_EQUAL(data.size(), 4u); - BOOST_CHECK_EQUAL(data, string("\007abc")); - data.clear(); - - dummy.i = boost::none; - Codec::encode(back_inserter(data))(dummy); - BOOST_CHECK_EQUAL(data, string("\6bc")); - data.clear(); - - const char* missing = "\5xy"; - Codec::decode(missing)(dummy); - BOOST_CHECK(dummy.i); - BOOST_CHECK_EQUAL(*dummy.i, 'x'); - BOOST_CHECK(!dummy.j); - BOOST_CHECK_EQUAL(dummy.k, 'y'); -} - -QPID_AUTO_TEST_CASE(testUnitControl) { - string data; - Control::Holder h(in_place<connection::Tune>(1,2,3,4)); - Codec::encode(std::back_inserter(data))(h); - - Unit unit(FrameHeader(FIRST_FRAME|LAST_FRAME, CONTROL)); - Codec::decode(data.begin())(unit); - - BOOST_REQUIRE(unit.get<ControlHolder>()); - - string data2; - Codec::encode(back_inserter(data2))(unit); - - BOOST_CHECK_EQUAL(data, data2); -} - -QPID_AUTO_TEST_CASE(testArray) { - ArrayDomain<char> a; - a.resize(3, 'x'); - string data; - Codec::encode(back_inserter(data))(a); - - ArrayDomain<char> b; - Codec::decode(data.begin())(b); - BOOST_CHECK_EQUAL(b.size(), 3u); - string data3; - Codec::encode(back_inserter(data3))(a); - BOOST_CHECK_EQUAL(data, data3); - - Array x; - Codec::decode(data.begin())(x); - BOOST_CHECK_EQUAL(x.size(), 3u); - BOOST_CHECK_EQUAL(x[0].size(), 1u); - BOOST_CHECK_EQUAL(*x[0].begin(), 'x'); - BOOST_CHECK_EQUAL(*x[2].begin(), 'x'); - - string data2; - Codec::encode(back_inserter(data2))(x); - BOOST_CHECK_EQUAL(data,data2); -} - -QPID_AUTO_TEST_CASE(testStruct) { - string data; - - message::DeliveryProperties dp; - BOOST_CHECK(!dp.discardUnroutable); - dp.immediate = true; - dp.redelivered = false; - dp.priority = message::MEDIUM; - dp.exchange = "foo"; - - Codec::encode(back_inserter(data))(dp); - // Skip 4 bytes size, little-endian decode for pack bits. - uint16_t encodedBits=uint8_t(data[5]); - encodedBits <<= 8; - encodedBits += uint8_t(data[4]); - BOOST_CHECK_EQUAL(encodedBits, packBits(dp)); - - data.clear(); - Struct32 h(dp); - Codec::encode(back_inserter(data))(h); - - Struct32 h2; - Codec::decode(data.begin())(h2); - BOOST_CHECK_EQUAL(h2.getClassCode(), Uint8(message::DeliveryProperties::CLASS_CODE)); - BOOST_CHECK_EQUAL(h2.getCode(), Uint8(message::DeliveryProperties::CODE)); - message::DeliveryProperties* dp2 = - dynamic_cast<message::DeliveryProperties*>(h2.get()); - BOOST_CHECK(dp2); - BOOST_CHECK(!dp2->discardUnroutable); - BOOST_CHECK(dp2->immediate); - BOOST_CHECK(!dp2->redelivered); - BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); - BOOST_CHECK_EQUAL(dp2->exchange, "foo"); -} - -struct RecodeUnit { - template <class T> - void operator() (const T& t) { - BOOST_MESSAGE(BOOST_CURRENT_FUNCTION << " called with: " << t); - using qpid::framing::Buffer; - using qpid::framing::AMQFrame; - - session::Header sh; - BOOST_CHECK_EQUAL(Codec::size(sh), 2u); - - // Encode unit. - Unit u(t); - string data; - Codec::encode(back_inserter(data))(u.getHeader())(u); - data.push_back(char(0xCE)); // Preview end-of-frame - - // Decode AMQFrame - Buffer buf(&data[0], data.size()); - AMQFrame f; - f.decode(buf); - BOOST_MESSAGE("AMQFrame decoded: " << f); - // Encode AMQFrame - string data2(f.size(), ' '); - Buffer buf2(&data2[0], data.size()); - f.encode(buf2); - - // Verify encoded by unit == encoded by AMQFrame - BOOST_CHECK_MESSAGE(data == data2, BOOST_CURRENT_FUNCTION); - - // Decode unit - // FIXME aconway 2008-04-15: must set limit to decode a header. - Codec::Decoder<string::iterator> decode(data2.begin(), data2.size()-1); - - FrameHeader h; - decode(h); - BOOST_CHECK_EQUAL(u.getHeader(), h); - Unit u2(h); - decode(u2); - - // Re-encode unit - string data3; - Codec::encode(back_inserter(data3))(u2.getHeader())(u2); - data3.push_back(char(0xCE)); // Preview end-of-frame - - BOOST_CHECK_MESSAGE(data3 == data2, BOOST_CURRENT_FUNCTION); - } -}; - -QPID_AUTO_TEST_CASE(testSerializeAllSegmentTypes) { - RecodeUnit recode; - allSegmentTypes(recode); -} - -QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/brokertest.py b/cpp/src/tests/brokertest.py index 3207a51b79..8255fbe9ac 100644 --- a/cpp/src/tests/brokertest.py +++ b/cpp/src/tests/brokertest.py @@ -436,6 +436,35 @@ class Cluster: def __getitem__(self,index): return self._brokers[index] def __iter__(self): return self._brokers.__iter__() + +def browse(session, queue, timeout=0, transform=lambda m: m.content): + """Return a list with the contents of each message on queue.""" + r = session.receiver("%s;{mode:browse}"%(queue)) + r.capacity = 100 + try: + contents = [] + try: + while True: contents.append(transform(r.fetch(timeout=timeout))) + except messaging.Empty: pass + finally: r.close() + return contents + +def assert_browse(session, queue, expect_contents, timeout=0, transform=lambda m: m.content, msg="browse failed"): + """Assert that the contents of messages on queue (as retrieved + using session and timeout) exactly match the strings in + expect_contents""" + actual_contents = browse(session, queue, timeout, transform=transform) + if msg: msg = "%s: %r != %r"%(msg, expect_contents, actual_contents) + assert expect_contents == actual_contents, msg + +def assert_browse_retry(session, queue, expect_contents, timeout=1, delay=.01, transform=lambda m:m.content, msg="browse failed"): + """Wait up to timeout for contents of queue to match expect_contents""" + test = lambda: browse(session, queue, 0, transform=transform) == expect_contents + retry(test, timeout, delay) + actual_contents = browse(session, queue, 0, transform=transform) + if msg: msg = "%s: %r != %r"%(msg, expect_contents, actual_contents) + assert expect_contents == actual_contents, msg + class BrokerTest(TestCase): """ Tracks processes started by test and kills at end of test. @@ -501,30 +530,9 @@ class BrokerTest(TestCase): cluster = Cluster(self, count, args, expect=expect, wait=wait, show_cmd=show_cmd) return cluster - def browse(self, session, queue, timeout=0, transform=lambda m: m.content): - """Return a list with the contents of each message on queue.""" - r = session.receiver("%s;{mode:browse}"%(queue)) - r.capacity = 100 - try: - contents = [] - try: - while True: contents.append(transform(r.fetch(timeout=timeout))) - except messaging.Empty: pass - finally: r.close() - return contents - - def assert_browse(self, session, queue, expect_contents, timeout=0, transform=lambda m: m.content): - """Assert that the contents of messages on queue (as retrieved - using session and timeout) exactly match the strings in - expect_contents""" - actual_contents = self.browse(session, queue, timeout, transform=transform) - self.assertEqual(expect_contents, actual_contents) - - def assert_browse_retry(self, session, queue, expect_contents, timeout=1, delay=.01, transform=lambda m:m.content): - """Wait up to timeout for contents of queue to match expect_contents""" - test = lambda: self.browse(session, queue, 0, transform=transform) == expect_contents - retry(test, timeout, delay) - self.assertEqual(expect_contents, self.browse(session, queue, 0, transform=transform)) + def browse(self, *args, **kwargs): browse(*args, **kwargs) + def assert_browse(self, *args, **kwargs): assert_browse(*args, **kwargs) + def assert_browse_retry(self, *args, **kwargs): assert_browse_retry(*args, **kwargs) def join(thread, timeout=10): thread.join(timeout) diff --git a/cpp/src/tests/cli_tests.py b/cpp/src/tests/cli_tests.py index 6c75927461..7ac5b1deed 100755 --- a/cpp/src/tests/cli_tests.py +++ b/cpp/src/tests/cli_tests.py @@ -22,7 +22,6 @@ import sys import os import imp from qpid.testlib import TestBase010 -# from brokertest import import_script, checkenv from qpid.datatypes import Message from qpid.queue import Empty from time import sleep @@ -61,14 +60,13 @@ class CliTests(TestBase010): ret = os.system(self.qpid_config_command(" add queue " + qname + " " + arguments)) self.assertEqual(ret, 0) - queues = self.qmf.getObjects(_class="queue") - for queue in queues: - if queue.name == qname: - return queue + queue = self.broker_access.getQueue(qname) + if queue: + return queue assert False def test_queue_params(self): - self.startQmf() + self.startBrokerAccess() queue1 = self.makeQueue("test_queue_params1", "--limit-policy none") queue2 = self.makeQueue("test_queue_params2", "--limit-policy reject") queue3 = self.makeQueue("test_queue_params3", "--limit-policy flow-to-disk") @@ -82,29 +80,21 @@ class CliTests(TestBase010): self.assertEqual(queue4.arguments[LIMIT], "ring") self.assertEqual(queue5.arguments[LIMIT], "ring_strict") - queue6 = self.makeQueue("test_queue_params6", "--order fifo") - queue7 = self.makeQueue("test_queue_params7", "--order lvq") - queue8 = self.makeQueue("test_queue_params8", "--order lvq-no-browse") - - LVQ = "qpid.last_value_queue" - LVQNB = "qpid.last_value_queue_no_browse" - - assert LVQ not in queue6.arguments - assert LVQ in queue7.arguments - assert LVQ not in queue8.arguments + queue6 = self.makeQueue("test_queue_params6", "--lvq-key lkey") - assert LVQNB not in queue6.arguments - assert LVQNB not in queue7.arguments - assert LVQNB in queue8.arguments + LVQKEY = "qpid.last_value_queue_key" + assert LVQKEY not in queue5.arguments + assert LVQKEY in queue6.arguments + assert queue6.arguments[LVQKEY] == "lkey" def test_queue_params_api(self): - self.startQmf() - queue1 = self.makeQueue("test_queue_params1", "--limit-policy none", True) - queue2 = self.makeQueue("test_queue_params2", "--limit-policy reject", True) - queue3 = self.makeQueue("test_queue_params3", "--limit-policy flow-to-disk", True) - queue4 = self.makeQueue("test_queue_params4", "--limit-policy ring", True) - queue5 = self.makeQueue("test_queue_params5", "--limit-policy ring-strict", True) + self.startBrokerAccess() + queue1 = self.makeQueue("test_queue_params_api1", "--limit-policy none", True) + queue2 = self.makeQueue("test_queue_params_api2", "--limit-policy reject", True) + queue3 = self.makeQueue("test_queue_params_api3", "--limit-policy flow-to-disk", True) + queue4 = self.makeQueue("test_queue_params_api4", "--limit-policy ring", True) + queue5 = self.makeQueue("test_queue_params_api5", "--limit-policy ring-strict", True) LIMIT = "qpid.policy_type" assert LIMIT not in queue1.arguments @@ -113,30 +103,22 @@ class CliTests(TestBase010): self.assertEqual(queue4.arguments[LIMIT], "ring") self.assertEqual(queue5.arguments[LIMIT], "ring_strict") - queue6 = self.makeQueue("test_queue_params6", "--order fifo", True) - queue7 = self.makeQueue("test_queue_params7", "--order lvq", True) - queue8 = self.makeQueue("test_queue_params8", "--order lvq-no-browse", True) + queue6 = self.makeQueue("test_queue_params_api6", "--lvq-key lkey") - LVQ = "qpid.last_value_queue" - LVQNB = "qpid.last_value_queue_no_browse" + LVQKEY = "qpid.last_value_queue_key" - assert LVQ not in queue6.arguments - assert LVQ in queue7.arguments - assert LVQ not in queue8.arguments - - assert LVQNB not in queue6.arguments - assert LVQNB not in queue7.arguments - assert LVQNB in queue8.arguments + assert LVQKEY not in queue5.arguments + assert LVQKEY in queue6.arguments + assert queue6.arguments[LVQKEY] == "lkey" def test_qpid_config(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); qname = "test_qpid_config" ret = os.system(self.qpid_config_command(" add queue " + qname)) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -146,7 +128,7 @@ class CliTests(TestBase010): ret = os.system(self.qpid_config_command(" del queue " + qname)) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -154,13 +136,12 @@ class CliTests(TestBase010): self.assertEqual(found, False) def test_qpid_config_api(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); qname = "test_qpid_config_api" ret = self.qpid_config_api(" add queue " + qname) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -170,7 +151,7 @@ class CliTests(TestBase010): ret = self.qpid_config_api(" del queue " + qname) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -179,25 +160,23 @@ class CliTests(TestBase010): def test_qpid_config_sasl_plain_expect_succeed(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); qname = "test_qpid_config_sasl_plain_expect_succeed" - cmd = " --sasl-mechanism PLAIN -a guest/guest@localhost:"+str(self.broker.port) + " add queue " + qname + cmd = " --sasl-mechanism PLAIN -b guest/guest@localhost:"+str(self.broker.port) + " add queue " + qname ret = self.qpid_config_api(cmd) self.assertEqual(ret, 0) def test_qpid_config_sasl_plain_expect_fail(self): """Fails because no user name and password is supplied""" - self.startQmf(); - qmf = self.qmf - qname = "test_qpid_config_sasl_plain_expect_succeed" - cmd = " --sasl-mechanism PLAIN -a localhost:"+str(self.broker.port) + " add queue " + qname + self.startBrokerAccess(); + qname = "test_qpid_config_sasl_plain_expect_fail" + cmd = " --sasl-mechanism PLAIN -b localhost:"+str(self.broker.port) + " add queue " + qname ret = self.qpid_config_api(cmd) assert ret != 0 # helpers for some of the test methods def helper_find_exchange(self, xchgname, typ, expected=True): - xchgs = self.qmf.getObjects(_class = "exchange") + xchgs = self.broker_access.getAllExchanges() found = False for xchg in xchgs: if xchg.name == xchgname: @@ -221,7 +200,7 @@ class CliTests(TestBase010): self.helper_find_exchange(xchgname, False, expected=False) def helper_find_queue(self, qname, expected=True): - queues = self.qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -246,8 +225,7 @@ class CliTests(TestBase010): # test the bind-queue-to-header-exchange functionality def test_qpid_config_headers(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); qname = "test_qpid_config" xchgname = "test_xchg" @@ -277,8 +255,7 @@ class CliTests(TestBase010): def test_qpid_config_xml(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); qname = "test_qpid_config" xchgname = "test_xchg" @@ -306,13 +283,12 @@ class CliTests(TestBase010): self.helper_destroy_exchange(xchgname) def test_qpid_config_durable(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); qname = "test_qpid_config" ret = os.system(self.qpid_config_command(" add queue --durable " + qname)) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -322,7 +298,7 @@ class CliTests(TestBase010): ret = os.system(self.qpid_config_command(" del queue " + qname)) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qname: @@ -330,8 +306,7 @@ class CliTests(TestBase010): self.assertEqual(found, False) def test_qpid_config_altex(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); exName = "testalt" qName = "testqalt" altName = "amq.direct" @@ -339,7 +314,7 @@ class CliTests(TestBase010): ret = os.system(self.qpid_config_command(" add exchange topic %s --alternate-exchange=%s" % (exName, altName))) self.assertEqual(ret, 0) - exchanges = qmf.getObjects(_class="exchange") + exchanges = self.broker_access.getAllExchanges() found = False for exchange in exchanges: if exchange.name == altName: @@ -349,20 +324,23 @@ class CliTests(TestBase010): found = True if not exchange.altExchange: self.fail("Alternate exchange not set") - self.assertEqual(exchange._altExchange_.name, altName) + self.assertEqual(exchange.altExchange, altName) self.assertEqual(found, True) ret = os.system(self.qpid_config_command(" add queue %s --alternate-exchange=%s" % (qName, altName))) self.assertEqual(ret, 0) - queues = qmf.getObjects(_class="queue") + ret = os.system(self.qpid_config_command(" queues")) + self.assertEqual(ret, 0) + + queues = self.broker_access.getAllQueues() found = False for queue in queues: if queue.name == qName: found = True if not queue.altExchange: self.fail("Alternate exchange not set") - self.assertEqual(queue._altExchange_.name, altName) + self.assertEqual(queue.altExchange, altName) self.assertEqual(found, True) def test_qpid_config_list_queues_arguments(self): @@ -371,8 +349,7 @@ class CliTests(TestBase010): actually a string (though still a valid value), it does not upset qpid-config """ - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); names = ["queue_capacity%s" % (i) for i in range(1, 6)] for name in names: @@ -386,15 +363,14 @@ class CliTests(TestBase010): assert name in queues, "%s not in %s" % (name, queues) def test_qpid_route(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); command = self.cli_dir() + "/qpid-route dynamic add guest/guest@localhost:%d %s:%d amq.topic" %\ (self.broker.port, self.remote_host(), self.remote_port()) ret = os.system(command) self.assertEqual(ret, 0) - links = qmf.getObjects(_class="link") + links = self.broker_access.getAllLinks() found = False for link in links: if link.port == self.remote_port(): @@ -402,8 +378,7 @@ class CliTests(TestBase010): self.assertEqual(found, True) def test_qpid_route_api(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); ret = self.qpid_route_api("dynamic add " + "guest/guest@localhost:"+str(self.broker.port) + " " @@ -412,7 +387,7 @@ class CliTests(TestBase010): self.assertEqual(ret, 0) - links = qmf.getObjects(_class="link") + links = self.broker_access.getAllLinks() found = False for link in links: if link.port == self.remote_port(): @@ -421,8 +396,7 @@ class CliTests(TestBase010): def test_qpid_route_api(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); ret = self.qpid_route_api("dynamic add " + " --client-sasl-mechanism PLAIN " @@ -432,7 +406,7 @@ class CliTests(TestBase010): self.assertEqual(ret, 0) - links = qmf.getObjects(_class="link") + links = self.broker_access.getAllLinks() found = False for link in links: if link.port == self.remote_port(): @@ -440,8 +414,7 @@ class CliTests(TestBase010): self.assertEqual(found, True) def test_qpid_route_api_expect_fail(self): - self.startQmf(); - qmf = self.qmf + self.startBrokerAccess(); ret = self.qpid_route_api("dynamic add " + " --client-sasl-mechanism PLAIN " @@ -463,11 +436,11 @@ class CliTests(TestBase010): return None def qpid_config_command(self, arg = ""): - return self.cli_dir() + "/qpid-config -a localhost:%d" % self.broker.port + " " + arg + return self.cli_dir() + "/qpid-config -b localhost:%d" % self.broker.port + " " + arg def qpid_config_api(self, arg = ""): script = import_script(checkenv("QPID_CONFIG_EXEC")) - broker = ["-a", "localhost:"+str(self.broker.port)] + broker = ["-b", "localhost:"+str(self.broker.port)] return script.main(broker + arg.split()) def qpid_route_api(self, arg = ""): diff --git a/cpp/src/tests/cluster.cmake b/cpp/src/tests/cluster.cmake index 3471173e97..31e2d337d1 100644 --- a/cpp/src/tests/cluster.cmake +++ b/cpp/src/tests/cluster.cmake @@ -55,7 +55,7 @@ add_test (clustered_replication_test ${CMAKE_CURRENT_SOURCE_DIR}/clustered_repli # CLEANFILES += cluster_test.acl cluster.ports # EXTRA_DIST += \ -# ais_check \ +# cpg_check.sh.in \ # run_cluster_test \ # cluster_read_credit \ # test_watchdog \ diff --git a/cpp/src/tests/cluster.mk b/cpp/src/tests/cluster.mk index 199d1e7b57..852b2dda8c 100644 --- a/cpp/src/tests/cluster.mk +++ b/cpp/src/tests/cluster.mk @@ -25,12 +25,11 @@ CLUSTER_TEST_SCRIPTS_LIST= \ allhosts rsynchosts \ qpid-build-rinstall qpid-src-rinstall \ - qpid-test-cluster \ - qpid-cluster-benchmark + qpid-test-cluster EXTRA_DIST += \ $(CLUSTER_TEST_SCRIPTS_LIST) \ - ais_check \ + cpg_check.sh.in \ run_cluster_test \ cluster_read_credit \ test_watchdog \ @@ -55,14 +54,12 @@ if HAVE_LIBCPG # # Cluster tests makefile fragment, to be included in Makefile.am -# +# # NOTE: Programs using the openais library must be run with gid=ais # You should do "newgrp ais" before running the tests to run these. -# - +# -# ais_check checks pre-requisites for cluster tests and runs them if ok. TESTS += \ run_cluster_test \ cluster_read_credit \ diff --git a/cpp/src/tests/cluster_failover b/cpp/src/tests/cluster_failover new file mode 100755 index 0000000000..43170c731a --- /dev/null +++ b/cpp/src/tests/cluster_failover @@ -0,0 +1,19 @@ +#!/bin/sh +# A simple manual failover test, sends a stream of numbered messages. +# You can kill the connected broker and verify that the clients reconnect +# and no messages are lost. + +URL=$1 +test -n "$URL" || { echo Usage: $0 URL ; exit 1; } +SEND=$(mktemp /tmp/send.XXXXXXXXXX) +RECV=$(mktemp /tmp/recv.XXXXXXXXXX) +echo $SEND $RECV + +seq 1000000 > $SEND + +qpid-send -a 'cluster_failover;{create:always}' -b $URL --connection-options "{reconnect:true}" --send-rate 10 --content-stdin < $SEND & + +while msg=$(qpid-receive -m1 -f -a 'cluster_failover;{create:always}' -b $URL --connection-options "{reconnect:true,heartbeat:1}"); do + echo -n $msg; date +done +wait diff --git a/cpp/src/tests/cluster_python_tests b/cpp/src/tests/cluster_python_tests index 8e17ffc8bc..25c7889246 100755 --- a/cpp/src/tests/cluster_python_tests +++ b/cpp/src/tests/cluster_python_tests @@ -20,7 +20,8 @@ # # Skip if cluster services not running. -. `dirname $0`/ais_check +. cpg_check.sh +cpg_enabled || exit 0 FAILING=`dirname $0`/cluster_python_tests_failing.txt source `dirname $0`/python_tests diff --git a/cpp/src/tests/cluster_read_credit b/cpp/src/tests/cluster_read_credit index fb3b72fbaf..552ffee53b 100755 --- a/cpp/src/tests/cluster_read_credit +++ b/cpp/src/tests/cluster_read_credit @@ -21,7 +21,9 @@ # Regression test for http://issues.apache.org/jira/browse/QPID-2086 srcdir=`dirname $0` -. $srcdir/ais_check +source cpg_check.sh +cpg_enabled || exit 0 + $srcdir/start_cluster 1 --cluster-read-max=2 || exit 1 trap $srcdir/stop_cluster EXIT seq 1 10000 | ./sender --port `cat cluster.ports` --routing-key no-such-queue diff --git a/cpp/src/tests/cluster_test_logs.py b/cpp/src/tests/cluster_test_logs.py index 3c7e8e8020..003d82c619 100755 --- a/cpp/src/tests/cluster_test_logs.py +++ b/cpp/src/tests/cluster_test_logs.py @@ -60,11 +60,13 @@ def filter_log(log): 'task late', 'task overran', 'warning CLOSING .* unsent data', - 'Inter-broker link ', + 'Inter-broker link ', # ignore link state changes + 'Updated link key from ', # ignore link state changes 'Running in a cluster, marking store', 'debug Sending keepalive signal to watchdog', # Watchdog timer thread 'last broker standing joined by 1 replicas, updating queue policies.', - 'Connection .* timed out: closing' # heartbeat connection close + 'Connection .* timed out: closing', # heartbeat connection close + "org.apache.qpid.broker:bridge:" # ignore bridge index ]) # Regex to match a UUID uuid='\w\w\w\w\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w\w\w\w\w\w\w\w\w' diff --git a/cpp/src/tests/cluster_tests.py b/cpp/src/tests/cluster_tests.py index cbc3df4a6b..8952f5de7b 100755 --- a/cpp/src/tests/cluster_tests.py +++ b/cpp/src/tests/cluster_tests.py @@ -277,7 +277,7 @@ acl deny all all QMF-based tools - regression test for BZ615300.""" broker1 = self.cluster(1)[0] broker2 = self.cluster(1)[0] - qs = subprocess.Popen(["qpid-stat", "-e", broker1.host_port()], stdout=subprocess.PIPE) + qs = subprocess.Popen(["qpid-stat", "-e", "-b", broker1.host_port()], stdout=subprocess.PIPE) out = qs.communicate()[0] assert out.find("amq.failover") > 0 @@ -767,6 +767,124 @@ acl deny all all cluster.start() fetch(cluster[2]) + + def test_federation_failover(self): + """ + Verify that federation operates across failures occuring in a cluster. + Specifically: + 1) Destination cluster learns of membership changes in the source + cluster + 2) Destination cluster replicates the current state of the source + cluster to newly-added members + """ + + TIMEOUT = 30 + def verify(src_broker, src, dst_broker, dst, timeout=TIMEOUT): + """ Prove that traffic can pass from source fed broker to + destination fed broker + """ + tot_time = 0 + active = False + send_session = src_broker.connect().session() + sender = send_session.sender(src) + receive_session = dst_broker.connect().session() + receiver = receive_session.receiver(dst) + while not active and tot_time < timeout: + sender.send(Message("Hello from Source!")) + try: + receiver.fetch(timeout = 1) + receive_session.acknowledge() + # Get this far without Empty exception, and the link is good! + active = True + while True: + # Keep receiving msgs, as several may have accumulated + receiver.fetch(timeout = 1) + receive_session.acknowledge() + except Empty: + if not active: + tot_time += 1 + receiver.close() + receive_session.close() + sender.close() + send_session.close() + self.assertTrue(active, "Bridge failed to become active") + + + # 2 node cluster source, 2 node cluster destination + src_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL) + src_cluster.ready(); + dst_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL) + dst_cluster.ready(); + + cmd = self.popen(["qpid-config", + "--broker", src_cluster[0].host_port(), + "add", "queue", "srcQ"], EXPECT_EXIT_OK) + cmd.wait() + + cmd = self.popen(["qpid-config", + "--broker", dst_cluster[0].host_port(), + "add", "exchange", "fanout", "destX"], EXPECT_EXIT_OK) + cmd.wait() + + cmd = self.popen(["qpid-config", + "--broker", dst_cluster[0].host_port(), + "add", "queue", "destQ"], EXPECT_EXIT_OK) + cmd.wait() + + cmd = self.popen(["qpid-config", + "--broker", dst_cluster[0].host_port(), + "bind", "destX", "destQ"], EXPECT_EXIT_OK) + cmd.wait() + + # federate the srcQ to the destination exchange + dst_cluster[0].startQmf() + dst_broker = dst_cluster[0].qmf_session.getObjects(_class="broker")[0] + result = dst_broker.connect(src_cluster[0].host(), src_cluster[0].port(), False, "PLAIN", + "guest", "guest", "tcp") + self.assertEqual(result.status, 0, result); + + link = dst_cluster[0].qmf_session.getObjects(_class="link")[0] + result = link.bridge(False, "srcQ", "destX", "", "", "", True, False, False, 10) + self.assertEqual(result.status, 0, result) + + # check that traffic passes + verify(src_cluster[0], "srcQ", dst_cluster[0], "destQ") + + # add src[2] broker to source cluster + src_cluster.start(expect=EXPECT_EXIT_FAIL); + src_cluster.ready(); + verify(src_cluster[2], "srcQ", dst_cluster[0], "destQ") + + # Kill src[0]. dst[0] should fail over to src[1] + src_cluster[0].kill() + for b in src_cluster[1:]: b.ready() + verify(src_cluster[1], "srcQ", dst_cluster[0], "destQ") + + # Kill src[1], dst[0] should fail over to src[2] + src_cluster[1].kill() + for b in src_cluster[2:]: b.ready() + verify(src_cluster[2], "srcQ", dst_cluster[0], "destQ") + + # Kill dest[0], force failover to dest[1] + dst_cluster[0].kill() + for b in dst_cluster[1:]: b.ready() + verify(src_cluster[2], "srcQ", dst_cluster[1], "destQ") + + # Add dest[2] + # dest[1] syncs dest[2] to current remote state + dst_cluster.start(expect=EXPECT_EXIT_FAIL); + for b in dst_cluster[1:]: b.ready() + verify(src_cluster[2], "srcQ", dst_cluster[1], "destQ") + + # Kill dest[1], force failover to dest[2] + dst_cluster[1].kill() + for b in dst_cluster[2:]: b.ready() + verify(src_cluster[2], "srcQ", dst_cluster[2], "destQ") + + for i in range(2, len(src_cluster)): src_cluster[i].kill() + for i in range(2, len(dst_cluster)): dst_cluster[i].kill() + + # Some utility code for transaction tests XA_RBROLLBACK = 1 XA_RBTIMEOUT = 2 @@ -1160,7 +1278,7 @@ class LongTests(BrokerTest): def start_mclients(broker): """Start management clients that make multiple connections.""" - cmd = ["qpid-stat", "-b", "localhost:%s" %(broker.port())] + cmd = ["qpid-cluster", "-C", "localhost:%s" %(broker.port())] mclients.append(ClientLoop(broker, cmd)) endtime = time.time() + self.duration() diff --git a/cpp/src/tests/clustered_replication_test b/cpp/src/tests/clustered_replication_test index 4a57502f39..5a9f143eb4 100755 --- a/cpp/src/tests/clustered_replication_test +++ b/cpp/src/tests/clustered_replication_test @@ -8,9 +8,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -34,11 +34,11 @@ stop_brokers() { if [[ $PRIMARY1 ]] ; then $QPIDD_EXEC --no-module-dir -q --port $PRIMARY1 unset PRIMARY1 - fi + fi if [[ $PRIMARY2 ]] ; then $QPIDD_EXEC --no-module-dir -q --port $PRIMARY2 unset PRIMARY2 - fi + fi if [[ $DR1 ]] ; then $QPIDD_EXEC --no-module-dir -q --port $DR1 unset DR1 @@ -50,7 +50,8 @@ stop_brokers() { } if test -d $PYTHON_DIR; then - . $srcdir/ais_check + . cpg_check.sh + cpg_enabled || exit 0 #todo: these cluster names need to be unique to prevent clashes PRIMARY_CLUSTER=PRIMARY_$(hostname)_$$ @@ -65,8 +66,8 @@ if test -d $PYTHON_DIR; then #start first node of primary cluster and set up test queue echo Starting primary cluster PRIMARY1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.1.tmp) || fail "Could not start PRIMARY1" - $PYTHON_COMMANDS/qpid-config -a "localhost:$PRIMARY1" add queue test-queue --generate-queue-events 2 - $PYTHON_COMMANDS/qpid-config -a "localhost:$PRIMARY1" add queue control-queue --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -b "localhost:$PRIMARY1" add queue test-queue --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -b "localhost:$PRIMARY1" add queue control-queue --generate-queue-events 1 #send 10 messages, consume 5 of them for i in `seq 1 10`; do echo Message$i; done | ./sender --port $PRIMARY1 @@ -81,28 +82,28 @@ if test -d $PYTHON_DIR; then DR1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.1.tmp) || fail "Could not start DR1" DR2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.2.tmp) || fail "Could not start DR2" - $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add queue test-queue - $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add queue control-queue - $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add exchange replication REPLICATION_EXCHANGE + $PYTHON_COMMANDS/qpid-config -b "localhost:$DR1" add queue test-queue + $PYTHON_COMMANDS/qpid-config -b "localhost:$DR1" add queue control-queue + $PYTHON_COMMANDS/qpid-config -b "localhost:$DR1" add exchange replication REPLICATION_EXCHANGE $PYTHON_COMMANDS/qpid-route queue add localhost:$DR2 localhost:$PRIMARY2 REPLICATION_EXCHANGE REPLICATION_QUEUE || fail "Could not add route." #send more messages to primary for i in `seq 11 20`; do echo Message$i; done | ./sender --port $PRIMARY1 --send-eos 1 - #wait for replication events to all be processed: + #wait for replication events to all be processed: echo Waiting for replication to complete echo Done | ./sender --port $PRIMARY1 --routing-key control-queue --send-eos 1 ./receiver --queue control-queue --port $DR1 > /dev/null #verify contents of test queue on dr cluster: - echo Verifying... + echo Verifying... ./receiver --port $DR2 > repl.out.tmp for i in `seq 6 20`; do echo Message$i; done | diff repl.out.tmp - || FAIL=1 if [[ $FAIL ]]; then echo Clustered replication test failed: expectations not met! exit 1 - else + else echo Clustered replication test passed rm -f repl*.tmp fi diff --git a/cpp/src/tests/ais_check b/cpp/src/tests/cpg_check.sh.in index a865260543..ed97776218 100755 --- a/cpp/src/tests/ais_check +++ b/cpp/src/tests/cpg_check.sh.in @@ -7,9 +7,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,12 +18,16 @@ # under the License. # -srcdir=`dirname $0` +QPID_USE_CPG=${QPID_USE_CPG:-@USE_CPG@} -# Check AIS requirements and run tests if found. -ps -u root | grep 'aisexec\|corosync' >/dev/null || { - echo WARNING: Skipping cluster tests, the aisexec or corosync daemon is not running. - exit 0; # A warning, not a failure. +# Check if CPG is enabled +cpg_enabled() { + test x$QPID_USE_CPG = xyes || return 1 # disabled + ps -u root | grep 'aisexec\|corosync' >/dev/null || { + echo WARNING: Skip cluster tests, aisexec or corosync daemon is not running. + return 1; # A warning, not a failure. + } + return 0 } # Execute command with the ais group set if user is a member. diff --git a/cpp/src/tests/federated_cluster_test b/cpp/src/tests/federated_cluster_test index b32455259e..f42b7501b8 100755 --- a/cpp/src/tests/federated_cluster_test +++ b/cpp/src/tests/federated_cluster_test @@ -8,9 +8,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -35,7 +35,7 @@ stop_brokers() { if [[ $BROKER_A ]] ; then ../qpidd --no-module-dir -q --port $BROKER_A unset BROKER_A - fi + fi if [[ $NODE_1 ]] ; then ../qpidd --no-module-dir -q --port $NODE_1 unset NODE_1 @@ -63,20 +63,20 @@ start_brokers() { setup() { #create exchange on both cluster and single broker - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add exchange direct test-exchange - $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" add exchange direct test-exchange + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add exchange direct test-exchange + $PYTHON_COMMANDS/qpid-config -b "localhost:$NODE_1" add exchange direct test-exchange #create dynamic routes for test exchange $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$NODE_2" "localhost:$BROKER_A" test-exchange $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$BROKER_A" "localhost:$NODE_2" test-exchange #create test queue on cluster and bind it to the test exchange - $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" add queue test-queue - $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" bind test-exchange test-queue to-cluster + $PYTHON_COMMANDS/qpid-config -b "localhost:$NODE_1" add queue test-queue + $PYTHON_COMMANDS/qpid-config -b "localhost:$NODE_1" bind test-exchange test-queue to-cluster #create test queue on single broker and bind it to the test exchange - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue test-queue - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" bind test-exchange test-queue from-cluster + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue test-queue + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" bind test-exchange test-queue from-cluster } run_test_pull_to_cluster_two_consumers() { @@ -92,7 +92,7 @@ run_test_pull_to_cluster_two_consumers() { wait sort -g -k 2 fed1.out.tmp fed2.out.tmp > fed.out.tmp diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!" - + rm -f fed*.tmp #cleanup } @@ -106,7 +106,7 @@ run_test_pull_to_cluster() { #verify all messages are received diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!" - + rm -f fed*.tmp #cleanup } @@ -121,22 +121,23 @@ run_test_pull_from_cluster() { #verify all messages are received wait diff fed.in.tmp fed.out.tmp || fail "federated link from cluster failed: expectations not met!" - + rm -f fed*.tmp #cleanup } if test -d ${PYTHON_DIR}; then - . $srcdir/ais_check + . cpg_check.sh + cpg_enabled || exit 0 rm -f fed*.tmp #cleanup any files left from previous run start_brokers echo "brokers started" setup echo "setup completed" - run_test_pull_to_cluster_two_consumers + run_test_pull_to_cluster_two_consumers echo "federated link to cluster verified" - run_test_pull_from_cluster + run_test_pull_from_cluster echo "federated link from cluster verified" if [[ $TEST_NODE_FAILURE ]] ; then #kill first cluster node and retest @@ -146,7 +147,7 @@ if test -d ${PYTHON_DIR}; then echo "retesting..." run_test_pull_to_cluster echo "federated link to cluster verified" - run_test_pull_from_cluster - echo "federated link from cluster verified" + run_test_pull_from_cluster + echo "federated link from cluster verified" fi fi diff --git a/cpp/src/tests/ha_tests.py b/cpp/src/tests/ha_tests.py index 97de0d1f77..827cb7dca9 100755 --- a/cpp/src/tests/ha_tests.py +++ b/cpp/src/tests/ha_tests.py @@ -18,71 +18,125 @@ # under the License. # -import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil -from qpid.messaging import Message, NotFound, ConnectionError, Connection +import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math +from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection +from qpid.datatypes import uuid4 from brokertest import * from threading import Thread, Lock, Condition from logging import getLogger, WARN, ERROR, DEBUG +from qpidtoollibs import BrokerAgent - -log = getLogger("qpid.ha-tests") +log = getLogger(__name__) class HaBroker(Broker): - def __init__(self, test, args=[], broker_url=None, **kwargs): + def __init__(self, test, args=[], broker_url=None, ha_cluster=True, + ha_replicate="all", **kwargs): assert BrokerTest.ha_lib, "Cannot locate HA plug-in" - args=["--load-module", BrokerTest.ha_lib, - # FIXME aconway 2012-02-13: workaround slow link failover. - "--link-maintenace-interval=0.1", - "--ha-enable=yes"] - if broker_url: args += [ "--ha-broker-url", broker_url ] + args = copy(args) + args += ["--load-module", BrokerTest.ha_lib, + "--log-enable=info+", "--log-enable=debug+:ha::", + # FIXME aconway 2012-02-13: workaround slow link failover. + "--link-maintenace-interval=0.1", + "--ha-cluster=%s"%ha_cluster] + if ha_replicate is not None: + args += [ "--ha-replicate=%s"%ha_replicate ] + if broker_url: args.extend([ "--ha-brokers", broker_url ]) Broker.__init__(self, test, args, **kwargs) + self.commands=os.getenv("PYTHON_COMMANDS") + assert os.path.isdir(self.commands) + getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover. def promote(self): - assert os.system("qpid-ha-tool --promote %s"%(self.host_port())) == 0 + assert os.system("%s/qpid-ha promote -b %s"%(self.commands, self.host_port())) == 0 def set_client_url(self, url): assert os.system( - "qpid-ha-tool --client-addresses=%s %s"%(url,self.host_port())) == 0 + "%s/qpid-ha set --public-brokers=%s -b %s"%(self.commands, url,self.host_port())) == 0 def set_broker_url(self, url): assert os.system( - "qpid-ha-tool --broker-addresses=%s %s"%(url, self.host_port())) == 0 - - -class ShortTests(BrokerTest): - """Short HA functionality tests.""" - - # Wait for an address to become valid. - def wait(self, session, address): - def check(): - try: - session.sender(address) - return True - except NotFound: return False - assert retry(check), "Timed out waiting for %s"%(address) - - # Wait for address to become valid on a backup broker. - def wait_backup(self, backup, address): - bs = self.connect_admin(backup).session() - self.wait(bs, address) - bs.connection.close() - - # Combines wait_backup and assert_browse_retry - def assert_browse_backup(self, backup, queue, expected, **kwargs): - bs = self.connect_admin(backup).session() - self.wait(bs, queue) - self.assert_browse_retry(bs, queue, expected, **kwargs) - bs.connection.close() - - def assert_missing(self, session, address): + "%s/qpid-ha set --brokers=%s -b %s"%(self.commands, url, self.host_port())) == 0 + + def replicate(self, from_broker, queue): + assert os.system( + "%s/qpid-ha replicate -b %s %s %s"%(self.commands, self.host_port(), from_broker, queue)) == 0 + + def config_replicate(self, from_broker, queue): + assert os.system( + "%s/qpid-config --broker=%s add queue --start-replica %s %s"%(self.commands, self.host_port(), from_broker, queue)) == 0 + + def config_declare(self, queue, replication): + assert os.system( + "%s/qpid-config --broker=%s add queue %s --replicate %s"%(self.commands, self.host_port(), queue, replication)) == 0 + + def connect_admin(self, **kwargs): + return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs) + + def wait_backup(self, address): + """Wait for address to become valid on a backup broker.""" + bs = self.connect_admin().session() + try: wait_address(bs, address) + finally: bs.connection.close() + + def assert_browse_backup(self, queue, expected, **kwargs): + """Combines wait_backup and assert_browse_retry.""" + bs = self.connect_admin().session() try: - session.receiver(address) - self.fail("Should not have been replicated: %s"%(address)) - except NotFound: pass + wait_address(bs, queue) + assert_browse_retry(bs, queue, expected, **kwargs) + finally: bs.connection.close() + +class HaCluster(object): + _cluster_count = 0 + + def __init__(self, test, n, **kwargs): + """Start a cluster of n brokers""" + self.test = test + self._brokers = [ HaBroker(test, name="broker%s-%s"%(HaCluster._cluster_count, i), **kwargs) for i in xrange(n)] + HaCluster._cluster_count += 1 + self.url = ",".join([b.host_port() for b in self]) + for b in self: b.set_broker_url(self.url) + self[0].promote() + + def connect(self, i): + """Connect with reconnect_urls""" + return self[i].connect(reconnect=True, reconnect_urls=self.url.split(",")) + + def kill(self, i): + """Kill broker i, promote broker i+1""" + self[i].kill() + self[i].expect = EXPECT_EXIT_FAIL + self[(i+1) % len(self)].promote() + + def bounce(self, i): + """Stop and restart a broker in a cluster.""" + self.kill(i) + b = self[i] + self._brokers[i] = HaBroker(self.test, name=b.name, port=b.port(), broker_url=self.url) + + # Behave like a list of brokers. + def __len__(self): return len(self._brokers) + def __getitem__(self,index): return self._brokers[index] + def __iter__(self): return self._brokers.__iter__() + +def wait_address(session, address): + """Wait for an address to become valid.""" + def check(): + try: + session.sender(address) + return True + except NotFound: return False + assert retry(check), "Timed out waiting for address %s"%(address) + +def assert_missing(session, address): + """Assert that the address is _not_ valid""" + try: + session.receiver(address) + self.fail("Expected NotFound: %s"%(address)) + except NotFound: pass - def connect_admin(self, backup, **kwargs): - """Connect to a backup broker as an admin connection""" - return backup.connect(client_properties={"qpid.ha-admin":1}, **kwargs) +class ReplicationTests(BrokerTest): + """Correctness tests for HA replication.""" def test_replication(self): """Test basic replication of configuration and messages before and @@ -95,21 +149,21 @@ class ShortTests(BrokerTest): return"%s;{create:always,node:{type:topic,x-declare:{arguments:{'qpid.replicate':%s}, type:'fanout'},x-bindings:[{exchange:'%s',queue:'%s'}]}}"%(name, replicate, name, bindq) def setup(p, prefix, primary): """Create config, send messages on the primary p""" - s = p.sender(queue(prefix+"q1", "messages")) + s = p.sender(queue(prefix+"q1", "all")) for m in ["a", "b", "1"]: s.send(Message(m)) # Test replication of dequeue self.assertEqual(p.receiver(prefix+"q1").fetch(timeout=0).content, "a") p.acknowledge() p.sender(queue(prefix+"q2", "configuration")).send(Message("2")) p.sender(queue(prefix+"q3", "none")).send(Message("3")) - p.sender(exchange(prefix+"e1", "messages", prefix+"q1")).send(Message("4")) - p.sender(exchange(prefix+"e2", "messages", prefix+"q2")).send(Message("5")) + p.sender(exchange(prefix+"e1", "all", prefix+"q1")).send(Message("4")) + p.sender(exchange(prefix+"e2", "all", prefix+"q2")).send(Message("5")) # Test unbind - p.sender(queue(prefix+"q4", "messages")).send(Message("6")) - s3 = p.sender(exchange(prefix+"e4", "messages", prefix+"q4")) + p.sender(queue(prefix+"q4", "all")).send(Message("6")) + s3 = p.sender(exchange(prefix+"e4", "all", prefix+"q4")) s3.send(Message("7")) # Use old connection to unbind - us = primary.connect_old().session(str(qpid.datatypes.uuid4())) + us = primary.connect_old().session(str(uuid4())) us.exchange_unbind(exchange=prefix+"e4", binding_key="", queue=prefix+"q4") p.sender(prefix+"e4").send(Message("drop1")) # Should be dropped # Need a marker so we can wait till sync is done. @@ -117,9 +171,8 @@ class ShortTests(BrokerTest): def verify(b, prefix, p): """Verify setup was replicated to backup b""" - # Wait for configuration to replicate. - self.wait(b, prefix+"x"); + wait_address(b, prefix+"x"); self.assert_browse_retry(b, prefix+"q1", ["b", "1", "4"]) self.assertEqual(p.receiver(prefix+"q1").fetch(timeout=0).content, "b") @@ -127,7 +180,7 @@ class ShortTests(BrokerTest): self.assert_browse_retry(b, prefix+"q1", ["1", "4"]) self.assert_browse_retry(b, prefix+"q2", []) # configuration only - self.assert_missing(b, prefix+"q3") + assert_missing(b, prefix+"q3") b.sender(prefix+"e1").send(Message(prefix+"e1")) # Verify binds with replicate=all self.assert_browse_retry(b, prefix+"q1", ["1", "4", prefix+"e1"]) b.sender(prefix+"e2").send(Message(prefix+"e2")) # Verify binds with replicate=configuration @@ -147,12 +200,12 @@ class ShortTests(BrokerTest): setup(p, "2", primary) # Verify the data on the backup - b = self.connect_admin(backup).session() + b = backup.connect_admin().session() verify(b, "1", p) verify(b, "2", p) # Test a series of messages, enqueue all then dequeue all. - s = p.sender(queue("foo","messages")) - self.wait(b, "foo") + s = p.sender(queue("foo","all")) + wait_address(b, "foo") msgs = [str(i) for i in range(10)] for m in msgs: s.send(Message(m)) self.assert_browse_retry(p, "foo", msgs) @@ -173,16 +226,11 @@ class ShortTests(BrokerTest): self.assert_browse_retry(p, "foo", msgs[i+1:]) self.assert_browse_retry(b, "foo", msgs[i+1:]) - def qpid_replicate(self, value="messages"): - return "node:{x-declare:{arguments:{'qpid.replicate':%s}}}" % value - def test_sync(self): - def queue(name, replicate): - return "%s;{create:always,%s}"%(name, self.qpid_replicate(replicate)) primary = HaBroker(self, name="primary") primary.promote() p = primary.connect().session() - s = p.sender(queue("q","messages")) + s = p.sender("q;{create:always}") for m in [str(i) for i in range(0,10)]: s.send(m) s.sync() backup1 = HaBroker(self, name="backup1", broker_url=primary.host_port()) @@ -193,49 +241,39 @@ class ShortTests(BrokerTest): s.sync() msgs = [str(i) for i in range(30)] - b1 = self.connect_admin(backup1).session() - self.wait(b1, "q"); + b1 = backup1.connect_admin().session() + wait_address(b1, "q"); self.assert_browse_retry(b1, "q", msgs) - b2 = self.connect_admin(backup2).session() - self.wait(b2, "q"); + b2 = backup2.connect_admin().session() + wait_address(b2, "q"); self.assert_browse_retry(b2, "q", msgs) def test_send_receive(self): """Verify sequence numbers of messages sent by qpid-send""" - primary = HaBroker(self, name="primary") - primary.promote() - backup1 = HaBroker(self, name="backup1", broker_url=primary.host_port()) - backup2 = HaBroker(self, name="backup2", broker_url=primary.host_port()) + brokers = HaCluster(self, 3) sender = self.popen( ["qpid-send", - "--broker", primary.host_port(), - "--address", "q;{create:always,%s}"%(self.qpid_replicate("messages")), + "--broker", brokers[0].host_port(), + "--address", "q;{create:always}", "--messages=1000", "--content-string=x" ]) receiver = self.popen( ["qpid-receive", - "--broker", primary.host_port(), - "--address", "q;{create:always,%s}"%(self.qpid_replicate("messages")), + "--broker", brokers[0].host_port(), + "--address", "q;{create:always}", "--messages=990", "--timeout=10" ]) - try: - self.assertEqual(sender.wait(), 0) - self.assertEqual(receiver.wait(), 0) - expect = [long(i) for i in range(991, 1001)] - sn = lambda m: m.properties["sn"] - self.assert_browse_retry(self.connect_admin(backup1).session(), "q", expect, transform=sn) - self.assert_browse_retry(self.connect_admin(backup2).session(), "q", expect, transform=sn) - except: - print self.browse(primary.connect().session(), "q", transform=sn) - print self.browse(self.connect_admin(backup1).session(), "q", transform=sn) - print self.browse(self.connect_admin(backup2).session(), "q", transform=sn) - raise + self.assertEqual(sender.wait(), 0) + self.assertEqual(receiver.wait(), 0) + expect = [long(i) for i in range(991, 1001)] + sn = lambda m: m.properties["sn"] + brokers[1].assert_browse_backup("q", expect, transform=sn) + brokers[2].assert_browse_backup("q", expect, transform=sn) def test_failover_python(self): """Verify that backups rejects connections and that fail-over works in python client""" - getLogger().setLevel(ERROR) # Disable WARNING log messages due to failover messages primary = HaBroker(self, name="primary", expect=EXPECT_EXIT_FAIL) primary.promote() backup = HaBroker(self, name="backup", broker_url=primary.host_port()) @@ -245,13 +283,13 @@ class ShortTests(BrokerTest): self.fail("Expected connection to backup to fail") except ConnectionError: pass # Check that admin connections are allowed to backup. - self.connect_admin(backup).close() + backup.connect_admin().close() # Test discovery: should connect to primary after reject by backup c = backup.connect(reconnect_urls=[primary.host_port(), backup.host_port()], reconnect=True) s = c.session() - sender = s.sender("q;{create:always,%s}"%(self.qpid_replicate())) - self.wait_backup(backup, "q") + sender = s.sender("q;{create:always}") + backup.wait_backup("q") sender.send("foo") primary.kill() assert retry(lambda: not is_running(primary.pid)) @@ -265,14 +303,14 @@ class ShortTests(BrokerTest): primary.promote() backup = HaBroker(self, name="backup", broker_url=primary.host_port()) url="%s,%s"%(primary.host_port(), backup.host_port()) - primary.connect().session().sender("q;{create:always,%s}"%(self.qpid_replicate())) - self.wait_backup(backup, "q") + primary.connect().session().sender("q;{create:always}") + backup.wait_backup("q") sender = NumberedSender(primary, url=url, queue="q", failover_updates = False) receiver = NumberedReceiver(primary, url=url, queue="q", failover_updates = False) receiver.start() sender.start() - self.wait_backup(backup, "q") + backup.wait_backup("q") assert retry(lambda: receiver.received > 10) # Wait for some messages to get thru primary.kill() @@ -284,19 +322,276 @@ class ShortTests(BrokerTest): receiver.stop() def test_backup_failover(self): - brokers = [ HaBroker(self, name=name, expect=EXPECT_EXIT_FAIL) - for name in ["a","b","c"] ] - url = ",".join([b.host_port() for b in brokers]) - for b in brokers: b.set_broker_url(url) - brokers[0].promote() - brokers[0].connect().session().sender( - "q;{create:always,%s}"%(self.qpid_replicate())).send("a") - for b in brokers[1:]: self.assert_browse_backup(b, "q", ["a"]) - brokers[0].kill() - brokers[2].promote() # c must fail over to b. - brokers[2].connect().session().sender("q").send("b") - self.assert_browse_backup(brokers[1], "q", ["a","b"]) - for b in brokers[1:]: b.kill() + """Verify that a backup broker fails over and recovers queue state""" + brokers = HaCluster(self, 3) + brokers[0].connect().session().sender("q;{create:always}").send("a") + for b in brokers[1:]: b.assert_browse_backup("q", ["a"], msg=b) + brokers[0].expect = EXPECT_EXIT_FAIL + brokers.kill(0) + brokers[1].connect().session().sender("q").send("b") + brokers[2].assert_browse_backup("q", ["a","b"]) + s = brokers[1].connect().session() + self.assertEqual("a", s.receiver("q").fetch().content) + s.acknowledge() + brokers[2].assert_browse_backup("q", ["b"]) + + def test_qpid_config_replication(self): + """Set up replication via qpid-config""" + brokers = HaCluster(self,2) + brokers[0].config_declare("q","all") + brokers[0].connect().session().sender("q").send("foo") + brokers[1].assert_browse_backup("q", ["foo"]) + + def test_standalone_queue_replica(self): + """Test replication of individual queues outside of cluster mode""" + primary = HaBroker(self, name="primary", ha_cluster=False) + pc = primary.connect() + ps = pc.session().sender("q;{create:always}") + pr = pc.session().receiver("q;{create:always}") + backup = HaBroker(self, name="backup", ha_cluster=False) + br = backup.connect().session().receiver("q;{create:always}") + + # Set up replication with qpid-ha + backup.replicate(primary.host_port(), "q") + ps.send("a") + backup.assert_browse_backup("q", ["a"]) + ps.send("b") + backup.assert_browse_backup("q", ["a", "b"]) + self.assertEqual("a", pr.fetch().content) + pr.session.acknowledge() + backup.assert_browse_backup("q", ["b"]) + + # Set up replication with qpid-config + ps2 = pc.session().sender("q2;{create:always}") + backup.config_replicate(primary.host_port(), "q2"); + ps2.send("x") + backup.assert_browse_backup("q2", ["x"]) + + + def test_queue_replica_failover(self): + """Test individual queue replication from a cluster to a standalone backup broker, verify it fails over.""" + cluster = HaCluster(self, 2) + primary = cluster[0] + pc = cluster.connect(0) + ps = pc.session().sender("q;{create:always}") + pr = pc.session().receiver("q;{create:always}") + backup = HaBroker(self, name="backup", ha_cluster=False) + br = backup.connect().session().receiver("q;{create:always}") + backup.replicate(cluster.url, "q") + ps.send("a") + backup.assert_browse_backup("q", ["a"]) + cluster.bounce(0) + backup.assert_browse_backup("q", ["a"]) + ps.send("b") + backup.assert_browse_backup("q", ["a", "b"]) + cluster.bounce(1) + self.assertEqual("a", pr.fetch().content) + pr.session.acknowledge() + backup.assert_browse_backup("q", ["b"]) + + def test_lvq(self): + """Verify that we replicate to an LVQ correctly""" + primary = HaBroker(self, name="primary") + primary.promote() + backup = HaBroker(self, name="backup", broker_url=primary.host_port()) + s = primary.connect().session().sender("lvq; {create:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':lvq-key}}}}") + def send(key,value): s.send(Message(content=value,properties={"lvq-key":key})) + for kv in [("a","a-1"),("b","b-1"),("a","a-2"),("a","a-3"),("c","c-1"),("c","c-2")]: + send(*kv) + backup.assert_browse_backup("lvq", ["b-1", "a-3", "c-2"]) + send("b","b-2") + backup.assert_browse_backup("lvq", ["a-3", "c-2", "b-2"]) + send("c","c-3") + backup.assert_browse_backup("lvq", ["a-3", "b-2", "c-3"]) + send("d","d-1") + backup.assert_browse_backup("lvq", ["a-3", "b-2", "c-3", "d-1"]) + + def test_ring(self): + """Test replication with the ring queue policy""" + primary = HaBroker(self, name="primary") + primary.promote() + backup = HaBroker(self, name="backup", broker_url=primary.host_port()) + s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5}}}}") + for i in range(10): s.send(Message(str(i))) + backup.assert_browse_backup("q", [str(i) for i in range(5,10)]) + + def test_reject(self): + """Test replication with the reject queue policy""" + primary = HaBroker(self, name="primary") + primary.promote() + backup = HaBroker(self, name="backup", broker_url=primary.host_port()) + s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':reject, 'qpid.max_count':5}}}}") + try: + for i in range(10): s.send(Message(str(i)), sync=False) + except qpid.messaging.exceptions.TargetCapacityExceeded: pass + backup.assert_browse_backup("q", [str(i) for i in range(0,5)]) + + def test_priority(self): + """Verify priority queues replicate correctly""" + primary = HaBroker(self, name="primary") + primary.promote() + backup = HaBroker(self, name="backup", broker_url=primary.host_port()) + session = primary.connect().session() + s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':10}}}}") + priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2] + for p in priorities: s.send(Message(priority=p)) + # Can't use browse_backup as browser sees messages in delivery order not priority. + backup.wait_backup("priority-queue") + r = backup.connect_admin().session().receiver("priority-queue") + received = [r.fetch().priority for i in priorities] + self.assertEqual(sorted(priorities, reverse=True), received) + + def test_priority_fairshare(self): + """Verify priority queues replicate correctly""" + primary = HaBroker(self, name="primary") + primary.promote() + backup = HaBroker(self, name="backup", broker_url=primary.host_port()) + session = primary.connect().session() + levels = 8 + priorities = [4,5,3,7,8,8,2,8,2,8,8,16,6,6,6,6,6,6,8,3,5,8,3,5,5,3,3,8,8,3,7,3,7,7,7,8,8,8,2,3] + limits={7:0,6:4,5:3,4:2,3:2,2:2,1:2} + limit_policy = ",".join(["'qpid.fairshare':5"] + ["'qpid.fairshare-%s':%s"%(i[0],i[1]) for i in limits.iteritems()]) + s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':%s, %s}}}}"%(levels,limit_policy)) + messages = [Message(content=str(uuid4()), priority = p) for p in priorities] + for m in messages: s.send(m) + backup.wait_backup(s.target) + r = backup.connect_admin().session().receiver("priority-queue") + received = [r.fetch().content for i in priorities] + sort = sorted(messages, key=lambda m: priority_level(m.priority, levels), reverse=True) + fair = [m.content for m in fairshare(sort, lambda l: limits.get(l,0), levels)] + self.assertEqual(received, fair) + + def test_priority_ring(self): + primary = HaBroker(self, name="primary") + primary.promote() + backup = HaBroker(self, name="backup", broker_url=primary.host_port()) + s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5, 'qpid.priorities':10}}}}") + priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2] + for p in priorities: s.send(Message(priority=p)) + + # FIXME aconway 2012-02-22: there is a bug in priority ring + # queues that allows a low priority message to displace a high + # one. The following commented-out assert_browse is for the + # correct result, the uncommented one is for the actualy buggy + # result. See https://issues.apache.org/jira/browse/QPID-3866 + # + # backup.assert_browse_backup("q", sorted(priorities,reverse=True)[0:5], transform=lambda m: m.priority) + backup.assert_browse_backup("q", [9,9,9,9,2], transform=lambda m: m.priority) + + def test_backup_acquired(self): + """Verify that acquired messages are backed up, for all queue types.""" + class Test: + def __init__(self, queue, arguments, expect): + self.queue = queue + self.address = "%s;{create:always,node:{x-declare:{arguments:{%s}}}}"%( + self.queue, ",".join(arguments + ["'qpid.replicate':all"])) + self.expect = [str(i) for i in expect] + + def send(self, connection): + """Send messages, then acquire one but don't acknowledge""" + s = connection.session() + for m in range(10): s.sender(self.address).send(str(m)) + s.receiver(self.address).fetch() + + def wait(self, brokertest, backup): + backup.wait_backup(self.queue) + + def verify(self, brokertest, backup): + backup.assert_browse_backup(self.queue, self.expect, msg=self.queue) + + tests = [ + Test("plain",[],range(10)), + Test("ring", ["'qpid.policy_type':ring", "'qpid.max_count':5"], range(5,10)), + Test("priority",["'qpid.priorities':10"], range(10)), + Test("fairshare", ["'qpid.priorities':10,'qpid.fairshare':5"], range(10)), + Test("lvq", ["'qpid.last_value_queue_key':lvq-key"], [9]) + ] + + primary = HaBroker(self, name="primary") + primary.promote() + backup1 = HaBroker(self, name="backup1", broker_url=primary.host_port()) + c = primary.connect() + for t in tests: t.send(c) # Send messages, leave one unacknowledged. + + backup2 = HaBroker(self, name="backup2", broker_url=primary.host_port()) + # Wait for backups to catch up. + for t in tests: + t.wait(self, backup1) + t.wait(self, backup2) + # Verify acquired message was replicated + for t in tests: t.verify(self, backup1) + for t in tests: t.verify(self, backup2) + + def test_replicate_default(self): + """Make sure we don't replicate if ha-replicate is unspecified or none""" + cluster1 = HaCluster(self, 2, ha_replicate=None) + c1 = cluster1[0].connect().session().sender("q;{create:always}") + cluster2 = HaCluster(self, 2, ha_replicate="none") + cluster2[0].connect().session().sender("q;{create:always}") + time.sleep(.1) # Give replication a chance. + try: + cluster1[1].connect_admin().session().receiver("q") + self.fail("Excpected no-such-queue exception") + except NotFound: pass + try: + cluster2[1].connect_admin().session().receiver("q") + self.fail("Excpected no-such-queue exception") + except NotFound: pass + + def test_invalid_default(self): + """Verify that a queue with an invalid qpid.replicate gets default treatment""" + cluster = HaCluster(self, 2, ha_replicate="all") + c = cluster[0].connect().session().sender("q;{create:always, node:{x-declare:{arguments:{'qpid.replicate':XXinvalidXX}}}}") + cluster[1].wait_backup("q") + + def test_exclusive_queue(self): + """Ensure that we can back-up exclusive queues, i.e. the replicating + subscriptions are exempt from the exclusivity""" + cluster = HaCluster(self, 2) + def test(addr): + c = cluster[0].connect() + q = addr.split(";")[0] + r = c.session().receiver(addr) + try: c.session().receiver(addr); self.fail("Expected exclusive exception") + except ReceiverError: pass + s = c.session().sender(q).send(q) + cluster[1].assert_browse_backup(q, [q]) + test("excl_sub;{create:always, link:{x-subscribe:{exclusive:True}}}"); + test("excl_queue;{create:always, node:{x-declare:{exclusive:True}}}") + +def fairshare(msgs, limit, levels): + """ + Generator to return prioritised messages in expected order for a given fairshare limit + """ + count = 0 + last_priority = None + postponed = [] + while msgs or postponed: + if not msgs: + msgs = postponed + count = 0 + last_priority = None + postponed = [] + msg = msgs.pop(0) + if last_priority and priority_level(msg.priority, levels) == last_priority: + count += 1 + else: + last_priority = priority_level(msg.priority, levels) + count = 1 + l = limit(last_priority) + if (l and count > l): + postponed.append(msg) + else: + yield msg + return + +def priority_level(value, levels): + """ + Method to determine which of a distinct number of priority levels + a given value falls into. + """ + offset = 5-math.ceil(levels/2.0) + return min(max(value - offset, 0), levels-1) class LongTests(BrokerTest): """Tests that can run for a long time if -DDURATION=<minutes> is set""" @@ -311,7 +606,7 @@ class LongTests(BrokerTest): """Test failover with continuous send-receive""" # FIXME aconway 2012-02-03: fails due to dropped messages, # known issue: sending messages to new primary before - # backups are ready. + # backups are ready. Enable when fixed. # Start a cluster, all members will be killed during the test. brokers = [ HaBroker(self, name=name, expect=EXPECT_EXIT_FAIL) @@ -352,4 +647,10 @@ class LongTests(BrokerTest): if __name__ == "__main__": shutil.rmtree("brokertest.tmp", True) - os.execvp("qpid-python-test", ["qpid-python-test", "-m", "ha_tests"] + sys.argv[1:]) + qpid_ha = os.getenv("QPID_HA_EXEC") + if qpid_ha and os.path.exists(qpid_ha): + os.execvp("qpid-python-test", + ["qpid-python-test", "-m", "ha_tests"] + sys.argv[1:]) + else: + print "Skipping ha_tests, qpid_ha not available" + diff --git a/cpp/src/tests/install_env.sh.in b/cpp/src/tests/install_env.sh.in index 2231954cb8..d29a23930d 100644 --- a/cpp/src/tests/install_env.sh.in +++ b/cpp/src/tests/install_env.sh.in @@ -23,4 +23,4 @@ prefix=`absdir @prefix@` export QPID_INSTALL_PREFIX=$prefix export PATH=$prefix/bin:$prefix/sbin:$prefix/libexec/qpid/tests:$PATH export LD_LIBRARY_PATH=$prefix/lib:$LD_LIBRARY_PATH -export PYTHONPATH=$prefix/lib/python2.4/site-packages:$PYTHONPATH +export PYTHONPATH=$prefix/lib/python2.6/site-packages:$PYTHONPATH diff --git a/cpp/src/tests/ipv6_test b/cpp/src/tests/ipv6_test index d75d50fd0a..6becfd8c96 100755 --- a/cpp/src/tests/ipv6_test +++ b/cpp/src/tests/ipv6_test @@ -93,10 +93,10 @@ else BROKER1="[::1]:${PORTS[1]}" TEST_QUEUE=ipv6-fed-test - $QPID_CONFIG_EXEC -a $BROKER0 add queue $TEST_QUEUE - $QPID_CONFIG_EXEC -a $BROKER1 add queue $TEST_QUEUE + $QPID_CONFIG_EXEC -b $BROKER0 add queue $TEST_QUEUE + $QPID_CONFIG_EXEC -b $BROKER1 add queue $TEST_QUEUE $QPID_ROUTE_EXEC dynamic add $BROKER1 $BROKER0 amq.direct - $QPID_CONFIG_EXEC -a $BROKER1 bind amq.direct $TEST_QUEUE $TEST_QUEUE + $QPID_CONFIG_EXEC -b $BROKER1 bind amq.direct $TEST_QUEUE $TEST_QUEUE $QPID_ROUTE_EXEC route map $BROKER1 ./datagen --count 100 | tee rdata-in | @@ -113,7 +113,8 @@ fi test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported. ## Test failover in a cluster using IPv6 only -. $srcdir/ais_check # Will exit if clustering not enabled. +. cpg_check.sh +cpg_enabled || exit 0 pick_port() { # We need a fixed port to set --cluster-url. Use qpidd to pick a free port. diff --git a/cpp/src/tests/logging.cpp b/cpp/src/tests/logging.cpp index fc55d642c3..5d5bb1feef 100644 --- a/cpp/src/tests/logging.cpp +++ b/cpp/src/tests/logging.cpp @@ -29,6 +29,7 @@ #endif #include <boost/test/floating_point_comparison.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <boost/format.hpp> #include "unit_test.h" @@ -43,8 +44,10 @@ namespace tests { QPID_AUTO_TEST_SUITE(loggingTestSuite) using namespace std; -using namespace boost; using namespace qpid::log; +using boost::ends_with; +using boost::contains; +using boost::format; QPID_AUTO_TEST_CASE(testStatementInit) { Statement s=QPID_LOG_STATEMENT_INIT(debug); int line=__LINE__; @@ -176,7 +179,9 @@ QPID_AUTO_TEST_CASE(testLoggerFormat) { l.format(Logger::FUNCTION); QPID_LOG(critical, "foo"); - BOOST_CHECK_EQUAL(string(BOOST_CURRENT_FUNCTION) + ": foo\n", out->last()); + BOOST_CHECK( ends_with( out->last(), ": foo\n")); + string name = out->last().substr(0, out->last().length() - 6); + BOOST_CHECK( contains( string(BOOST_CURRENT_FUNCTION), name)); l.format(Logger::LEVEL); QPID_LOG(critical, "foo"); diff --git a/cpp/src/tests/python_tests.ps1 b/cpp/src/tests/python_tests.ps1 index 9f8b9890c4..f7caa8f75a 100644 --- a/cpp/src/tests/python_tests.ps1 +++ b/cpp/src/tests/python_tests.ps1 @@ -26,8 +26,7 @@ if (!(Test-Path $PYTHON_DIR -pathType Container)) { exit 1 } -$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py" -$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py" +. .\test_env.ps1 if (Test-Path env:FAILING) { $fails = "-I $env:FAILING" @@ -39,7 +38,5 @@ else { $tests = "$args" } -#cd $PYTHON_DIR -$env:PYTHONPATH="$PYTHON_DIR;$PYTHON_TEST_DIR;$env:PYTHONPATH;$QMF_LIB" python $PYTHON_DIR/qpid-python-test -m qpid_tests.broker_0_10 -m qpid.tests -b localhost:$env:QPID_PORT $fails $tests exit $LASTEXITCODE diff --git a/cpp/src/tests/qpid-cluster-benchmark b/cpp/src/tests/qpid-cluster-benchmark index d836ed709c..610beacebd 100755 --- a/cpp/src/tests/qpid-cluster-benchmark +++ b/cpp/src/tests/qpid-cluster-benchmark @@ -22,38 +22,45 @@ # Default options MESSAGES="-m 10000" -FLOW="--flow-control 100" # Flow control limit on queue depth for latency. REPEAT="--repeat 10" QUEUES="-q 6" SENDERS="-s 3" RECEIVERS="-r 3" BROKERS= # Local broker CLIENT_HOSTS= # No ssh, all clients are local +# Connection options +TCP_NODELAY=false +RECONNECT=true +HEARTBEAT=1 -while getopts "m:f:n:b:q:s:r:c:txyv-" opt; do +while getopts "m:f:n:b:q:s:r:c:h:i:txyv-" opt; do case $opt in + b) BROKERS="-b $OPTARG";; + c) CLIENT_HOSTS="-c $OPTARG";; + h) HEARTBEAT=$OPTARG;; + i) RECONNECT=$OPTARG;; m) MESSAGES="-m $OPTARG";; - f) FLOW="--flow-control $OPTARG";; n) REPEAT="--repeat $OPTARG";; - b) BROKERS="-b $OPTARG";; q) QUEUES="-q $OPTARG";; - s) SENDERS="-s $OPTARG";; r) RECEIVERS="-r $OPTARG";; - c) CLIENT_HOSTS="-c $OPTARG";; - t) TCP_NODELAY="--connection-options {tcp-nodelay:true}";; + s) SENDERS="-s $OPTARG";; + t) TCP_NODELAY=true;; + v) OPTS="--verbose";; x) SAVE_RECEIVED="--save-received";; y) NO_DELETE="--no-delete";; - v) OPTS="--verbose";; -) break ;; *) echo "Unknown option"; exit 1;; esac done shift $(($OPTIND-1)) -REPLICATE="node:{x-declare:{arguments:{'qpid.replicate':all}}}" +CONNECTION_OPTIONS="--connection-options {tcp-nodelay:$TCP_NODELAY,reconnect:$RECONNECT,heartbeat:$HEARTBEAT}" +CREATE_OPTIONS="node:{x-declare:{arguments:{'qpid.replicate':all}}}" + BROKER=$(echo $BROKERS | sed s/,.*//) run_test() { echo $*; shift; "$@"; echo; echo; echo; } -OPTS="$OPTS $REPEAT $BROKERS --summarize $QUEUES $SENDERS $RECEIVERS $MESSAGES $CLIENT_HOSTS $SAVE_RECEIVED $TCP_NODELAY $NO_DELETE" -OPTS="$OPTS --create-option $REPLICATE" +OPTS="$OPTS $REPEAT $BROKERS --summarize $QUEUES $SENDERS $RECEIVERS $MESSAGES $CLIENT_HOSTS $SAVE_RECEIVED $CONNECTION_OPTIONS $NO_DELETE" +OPTS="$OPTS --create-option $CREATE_OPTIONS" + run_test "Benchmark:" qpid-cpp-benchmark $OPTS "$@" diff --git a/cpp/src/tests/qpid-cpp-benchmark b/cpp/src/tests/qpid-cpp-benchmark index 19c01dd08a..d5ad5191ca 100755 --- a/cpp/src/tests/qpid-cpp-benchmark +++ b/cpp/src/tests/qpid-cpp-benchmark @@ -37,7 +37,7 @@ op.add_option("-r", "--receivers", default=1, type="int", metavar="N", op.add_option("-m", "--messages", default=100000, type="int", metavar="N", help="send N messages per sender (default %default)") op.add_option("--queue-name", default="benchmark", metavar="NAME", - help="base name for queues (default %default)") + help="base name for queues (default %default)") op.add_option("--send-rate", default=0, metavar="N", help="send rate limited to N messages/second, 0 means no limit (default %default)") op.add_option("--receive-rate", default=0, metavar="N", @@ -67,18 +67,16 @@ op.add_option("--sequence", dest="sequence", default=False, action="store_true", help="add a sequence number to each message") op.add_option("--connection-options", type="str", help="Connection options for senders & receivers") -op.add_option("--flow-control", default=0, type="int", metavar="N", - help="Flow control each sender to limit queue depth to 2*N. 0 means no flow control.") op.add_option("--durable", default=False, action="store_true", help="Use durable queues and messages") op.add_option("--save-received", default=False, action="store_true", help="Save received message content to files <queuename>-receiver-<n>.msg") -op.add_option("--group-receivers", default=False, action="store_true", - help="Run receivers for the same queue on the same host.") op.add_option("--verbose", default=False, action="store_true", help="Show commands executed") op.add_option("--no-delete", default=False, action="store_true", help="Don't delete the test queues.") +op.add_option("--fill-drain", default=False, action="store_true", + help="First fill the queues, then drain them") single_quote_re = re.compile("'") def posix_quote(string): @@ -150,7 +148,6 @@ def start_send(queue, opts, broker, host): "--report-header=no", "--timestamp=%s"%(opts.timestamp and "yes" or "no"), "--sequence=%s"%(opts.sequence and "yes" or "no"), - "--flow-control", str(opts.flow_control), "--durable", str(opts.durable) ] command += opts.send_arg @@ -169,18 +166,6 @@ def first_line(p): raise Exception("Process exit %d: %s"%(p.returncode, error_msg(out,err))) return out.split("\n")[0] -def queue_exists(queue,broker): - c = qpid.messaging.Connection(broker) - c.open() - try: - s = c.session() - try: - s.sender(queue) - return True - except qpid.messaging.exceptions.NotFound: - return False - finally: c.close() - def recreate_queues(queues, brokers, no_delete, opts): c = qpid.messaging.Connection(brokers[0]) c.open() @@ -189,15 +174,9 @@ def recreate_queues(queues, brokers, no_delete, opts): if not no_delete: try: s.sender("%s;{delete:always}"%(q)).close() except qpid.messaging.exceptions.NotFound: pass - # FIXME aconway 2011-05-04: new cluster async wiring, wait for changes to propagate - for b in brokers: - while queue_exists(q,b): time.sleep(0.1); address = "%s;{%s}"%(q, ",".join(opts.create_option + ["create:always"])) if opts.verbose: print "Creating", address s.sender(address) - # FIXME aconway 2011-05-04: new cluster async wiring, wait for changes to propagate - for b in brokers: - while not queue_exists(q,b): time.sleep(0.1); c.close() def print_header(timestamp): @@ -295,24 +274,32 @@ def main(): recreate_queues(queues, opts.broker, opts.no_delete, opts) ready_receiver = ReadyReceiver(ready_queue, opts.broker[0]) - if opts.group_receivers: # Run receivers for same queue against same broker. - receivers = [] - for q in queues: - b = brokers.next() - for j in xrange(opts.receivers): - receivers.append( - start_receive(q, j, opts, ready_queue, b, client_hosts.next())) - else: # Don't group receivers - receivers = [start_receive(q, j, opts, ready_queue, - brokers.next(), client_hosts.next()) - for q in queues for j in xrange(opts.receivers)] - - ready_receiver.wait(filter(None, receivers)) # Wait for receivers to be ready. - start = time.time() - senders = [start_send(q, opts,brokers.next(), client_hosts.next()) - for q in queues for j in xrange(opts.senders)] + def start_receivers(): + return [ start_receive(q, j, opts, ready_queue, brokers.next(), client_hosts.next()) + for q in queues for j in xrange(opts.receivers) ] + + + def start_senders(): + return [ start_send(q, opts,brokers.next(), client_hosts.next()) + for q in queues for j in xrange(opts.senders) ] + if opts.report_header and i == 0: print_header(opts.timestamp) - for p in senders + receivers: p.wait() + + if opts.fill_drain: + # First fill the queues, then drain them + start = time.time() + senders = start_senders() + for p in senders: p.wait() + receivers = start_receivers() + for p in receivers: p.wait() + else: + # Run senders and receivers in parallel + receivers = start_receivers() + ready_receiver.wait(filter(None, receivers)) # Wait for receivers ready + start = time.time() + senders = start_senders() + for p in senders + receivers: p.wait() + total_sent = opts.queues * opts.senders * opts.messages total_tp = total_sent / (time.time()-start) send_stats=parse_senders(senders) diff --git a/cpp/src/tests/qpid-perftest.cpp b/cpp/src/tests/qpid-perftest.cpp index 664f0cf877..c28bdfe5f8 100644 --- a/cpp/src/tests/qpid-perftest.cpp +++ b/cpp/src/tests/qpid-perftest.cpp @@ -85,6 +85,7 @@ struct Opts : public TestOptions { size_t pubs; size_t count ; size_t size; + size_t headers; bool confirm; bool durable; bool uniqueData; @@ -112,7 +113,7 @@ struct Opts : public TestOptions { Opts() : TestOptions(helpText), setup(false), control(false), publish(false), subscribe(false), baseName("qpid-perftest"), - pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), syncPub(false), + pubs(1), count(500000), size(1024), headers(0), confirm(true), durable(false), uniqueData(false), syncPub(false), subs(1), ack(0), qt(1),singleConnect(false), iterations(1), mode(SHARED), summary(false), intervalSub(0), intervalPub(0), tx(0), txPub(0), txSub(0), commitAsync(false) @@ -131,6 +132,7 @@ struct Opts : public TestOptions { ("npubs", optValue(pubs, "N"), "Create N publishers.") ("count", optValue(count, "N"), "Each publisher sends N messages.") ("size", optValue(size, "BYTES"), "Size of messages in bytes.") + ("headers", optValue(headers, "N"), "Number of headers to add to each message.") ("pub-confirm", optValue(confirm, "yes|no"), "Publisher use confirm-mode.") ("durable", optValue(durable, "yes|no"), "Publish messages as durable.") ("unique-data", optValue(uniqueData, "yes|no"), "Make data for each message unique.") @@ -503,7 +505,13 @@ struct PublishThread : public Client { Message msg(data, routingKey); if (opts.durable) msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); - + if (opts.headers) { + for (size_t i = 0; i < opts.headers; ++i) { + std::stringstream h; + h << "hdr" << i; + msg.getMessageProperties().getApplicationHeaders().setString(h.str(), h.str()); + } + } if (opts.txPub){ session.txSelect(); diff --git a/cpp/src/tests/qpid-ping.cpp b/cpp/src/tests/qpid-ping.cpp index 0cb4afa0ee..52331499e7 100644 --- a/cpp/src/tests/qpid-ping.cpp +++ b/cpp/src/tests/qpid-ping.cpp @@ -32,11 +32,20 @@ #include <string> #include <iostream> -using namespace std; -using namespace qpid::sys; -using namespace qpid::framing; -using namespace qpid::client; -using namespace qpid; +using std::cerr; +using std::cout; +using std::endl; +using std::exception; +using std::string; +using namespace qpid::client::arg; // For keyword args +using qpid::client::AsyncSession; +using qpid::client::Connection; +using qpid::client::Message; +using qpid::client::SubscriptionManager; +using qpid::framing::Uuid; + +namespace qpid { +namespace tests { struct PingOptions : public qpid::TestOptions { int timeout; // Timeout in seconds. @@ -48,9 +57,11 @@ struct PingOptions : public qpid::TestOptions { } }; +}} // namespace qpid::tests + int main(int argc, char** argv) { try { - PingOptions opts; + qpid::tests::PingOptions opts; opts.parse(argc, argv); opts.con.heartbeat = (opts.timeout+1)/2; Connection connection; @@ -58,8 +69,8 @@ int main(int argc, char** argv) { if (!opts.quiet) cout << "Opened connection." << endl; AsyncSession s = connection.newSession(); string qname(Uuid(true).str()); - s.queueDeclare(arg::queue=qname,arg::autoDelete=true,arg::exclusive=true); - s.messageTransfer(arg::content=Message("hello", qname)); + s.queueDeclare(queue=qname, autoDelete=true, exclusive=true); + s.messageTransfer(content=Message("hello", qname)); if (!opts.quiet) cout << "Sent message." << endl; SubscriptionManager subs(s); subs.get(qname); diff --git a/cpp/src/tests/qpid-send.cpp b/cpp/src/tests/qpid-send.cpp index b1213a484f..b1c4f2be38 100644 --- a/cpp/src/tests/qpid-send.cpp +++ b/cpp/src/tests/qpid-send.cpp @@ -36,15 +36,26 @@ #include <iostream> #include <memory> -using namespace std; -using namespace qpid::messaging; -using namespace qpid::types; - -typedef std::vector<std::string> string_vector; +using std::string; +using std::ios_base; + +using qpid::messaging::Address; +using qpid::messaging::Connection; +using qpid::messaging::Duration; +using qpid::messaging::FailoverUpdates; +using qpid::messaging::Message; +using qpid::messaging::Receiver; +using qpid::messaging::Session; +using qpid::messaging::Sender; +using qpid::types::Exception; +using qpid::types::Uuid; +using qpid::types::Variant; namespace qpid { namespace tests { +typedef std::vector<std::string> string_vector; + struct Options : public qpid::Options { bool help; @@ -74,7 +85,6 @@ struct Options : public qpid::Options uint reportEvery; bool reportHeader; uint sendRate; - uint flowControl; bool sequence; bool timestamp; std::string groupKey; @@ -104,7 +114,6 @@ struct Options : public qpid::Options reportEvery(0), reportHeader(true), sendRate(0), - flowControl(0), sequence(true), timestamp(true), groupPrefix("GROUP-"), @@ -138,7 +147,6 @@ struct Options : public qpid::Options ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput statistics every N messages") ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.") ("send-rate", qpid::optValue(sendRate,"N"), "Send at rate of N messages/second. 0 means send as fast as possible.") - ("flow-control", qpid::optValue(flowControl,"N"), "Do end to end flow control to limit queue depth to 2*N. 0 means no flow control.") ("sequence", qpid::optValue(sequence, "yes|no"), "Add a sequence number messages property (required for duplicate/lost message detection)") ("timestamp", qpid::optValue(timestamp, "yes|no"), "Add a time stamp messages property (required for latency measurement)") ("group-key", qpid::optValue(groupKey, "KEY"), "Generate groups of messages using message header 'KEY' to hold the group identifier") @@ -223,10 +231,6 @@ const string EOS("eos"); const string SN("sn"); const string TS("ts"); -}} // namespace qpid::tests - -using namespace qpid::tests; - class ContentGenerator { public: virtual ~ContentGenerator() {} @@ -329,6 +333,20 @@ public: } }; +}} // namespace qpid::tests + +using qpid::tests::Options; +using qpid::tests::Reporter; +using qpid::tests::Throughput; +using qpid::tests::ContentGenerator; +using qpid::tests::GroupGenerator; +using qpid::tests::GetlineContentGenerator; +using qpid::tests::MapContentGenerator; +using qpid::tests::FixedContentGenerator; +using qpid::tests::SN; +using qpid::tests::TS; +using qpid::tests::EOS; + int main(int argc, char ** argv) { Connection connection; @@ -350,8 +368,6 @@ int main(int argc, char ** argv) msg.setPriority(opts.priority); } if (!opts.replyto.empty()) { - if (opts.flowControl) - throw Exception("Can't use reply-to and flow-control together"); msg.setReplyTo(Address(opts.replyto)); } if (!opts.userid.empty()) msg.setUserId(opts.userid); @@ -385,26 +401,10 @@ int main(int argc, char ** argv) int64_t interval = 0; if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate; - Receiver flowControlReceiver; - Address flowControlAddress("flow-"+Uuid(true).str()+";{create:always,delete:always}"); - uint flowSent = 0; - if (opts.flowControl) { - flowControlReceiver = session.createReceiver(flowControlAddress); - flowControlReceiver.setCapacity(2); - } - while (contentGen->setContent(msg)) { ++sent; if (opts.sequence) msg.getProperties()[SN] = sent; - if (opts.flowControl) { - if ((sent % opts.flowControl) == 0) { - msg.setReplyTo(flowControlAddress); - ++flowSent; - } - else - msg.setReplyTo(Address()); // Clear the reply address. - } if (groupGen.get()) groupGen->setGroupInfo(msg); @@ -423,19 +423,12 @@ int main(int argc, char ** argv) } if (opts.messages && sent >= opts.messages) break; - if (opts.flowControl && flowSent == 2) { - flowControlReceiver.get(Duration::SECOND); - --flowSent; - } - if (opts.sendRate) { qpid::sys::AbsTime waitTill(start, sent*interval); int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill); if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC); } } - for ( ; flowSent>0; --flowSent) - flowControlReceiver.get(Duration::SECOND); if (opts.reportTotal) reporter.report(); for (uint i = opts.sendEos; i > 0; --i) { if (opts.sequence) diff --git a/cpp/src/tests/qpid-txtest.cpp b/cpp/src/tests/qpid-txtest.cpp index d0ba2f1245..6e7d46802c 100644 --- a/cpp/src/tests/qpid-txtest.cpp +++ b/cpp/src/tests/qpid-txtest.cpp @@ -33,6 +33,7 @@ #include "qpid/client/SubscriptionManager.h" #include "qpid/framing/Array.h" #include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/Uuid.h" #include "qpid/sys/Thread.h" @@ -245,10 +246,10 @@ struct Controller : public Client // Recover DTX transactions (if any) if (opts.dtx) { - std::vector<std::string> inDoubtXids; framing::DtxRecoverResult dtxRes = session.dtxRecover().get(); const framing::Array& xidArr = dtxRes.getInDoubt(); - xidArr.collect(inDoubtXids); + std::vector<std::string> inDoubtXids(xidArr.size()); + std::transform(xidArr.begin(), xidArr.end(), inDoubtXids.begin(), framing::Array::get<std::string, framing::Array::ValuePtr>); if (inDoubtXids.size()) { if (!opts.quiet) std::cout << "Recovering DTX in-doubt transaction(s):" << std::endl; diff --git a/cpp/src/tests/qpidd-empty.conf b/cpp/src/tests/qpidd-empty.conf new file mode 100644 index 0000000000..bbf52bf446 --- /dev/null +++ b/cpp/src/tests/qpidd-empty.conf @@ -0,0 +1,3 @@ +# An empty configuration file. +# Used when running tests to avoid picking up configuration +# installed in the default place. diff --git a/cpp/src/tests/queue_flow_limit_tests.py b/cpp/src/tests/queue_flow_limit_tests.py index dec7cfb3af..d51b26a821 100644 --- a/cpp/src/tests/queue_flow_limit_tests.py +++ b/cpp/src/tests/queue_flow_limit_tests.py @@ -117,7 +117,7 @@ class QueueFlowLimitTests(TestBase010): tool = environ.get("QPID_CONFIG_EXEC") if tool: command = tool + \ - " --broker-addr=%s:%s " % (self.broker.host, self.broker.port) \ + " --broker=%s:%s " % (self.broker.host, self.broker.port) \ + "add queue test01 --flow-stop-count=999" \ + " --flow-resume-count=55 --flow-stop-size=5000000" \ + " --flow-resume-size=100000" diff --git a/cpp/src/tests/reliable_replication_test b/cpp/src/tests/reliable_replication_test index 1f1dac5f2d..c660f751e5 100755 --- a/cpp/src/tests/reliable_replication_test +++ b/cpp/src/tests/reliable_replication_test @@ -47,12 +47,12 @@ setup() { echo "Testing replication from port $BROKER_A to port $BROKER_B" - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication #create test queue (only replicate enqueues for this test): - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-a } send() { diff --git a/cpp/src/tests/replication_test b/cpp/src/tests/replication_test index 8c37568875..f8b2136396 100755 --- a/cpp/src/tests/replication_test +++ b/cpp/src/tests/replication_test @@ -46,21 +46,21 @@ if test -d ${PYTHON_DIR} && test -f "$REPLICATING_LISTENER_LIB" && test -f "$REP BROKER_B=`cat qpidd.port` echo "Running replication test between localhost:$BROKER_A and localhost:$BROKER_B" - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication #create test queues - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-b - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-c - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-a + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-b + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-c + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-e #queue-d deliberately not declared on DR; this error case should be handled #publish and consume from test queues on broker A: @@ -124,13 +124,13 @@ if test -d ${PYTHON_DIR} && test -f "$REPLICATING_LISTENER_LIB" && test -f "$REP $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port BROKER_B=`cat qpidd.port` - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1 - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-d + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-e + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1 + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-d i=1 while [ $i -le 10 ]; do @@ -152,8 +152,8 @@ if test -d ${PYTHON_DIR} && test -f "$REPLICATING_LISTENER_LIB" && test -f "$REP $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port BROKER_B=`cat qpidd.port` - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e - $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add queue queue-e + $PYTHON_COMMANDS/qpid-config -b "localhost:$BROKER_B" add exchange replication replication $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication # now send another 15 i=11 diff --git a/cpp/src/tests/ring_queue_test b/cpp/src/tests/ring_queue_test index 553746eb49..271b46183e 100755 --- a/cpp/src/tests/ring_queue_test +++ b/cpp/src/tests/ring_queue_test @@ -28,7 +28,7 @@ MESSAGES=10000 SENDERS=1 RECEIVERS=1 CONCURRENT=0 -BROKER_URL="-a ${QPID_BROKER:-localhost}:${QPID_PORT:-5672}" +BROKER_URL="-b ${QPID_BROKER:-localhost}:${QPID_PORT:-5672}" setup() { if [[ $DURABLE -gt 0 ]]; then diff --git a/cpp/src/tests/run_acl_tests b/cpp/src/tests/run_acl_tests index 41f41e20e1..3a8c03eda6 100755 --- a/cpp/src/tests/run_acl_tests +++ b/cpp/src/tests/run_acl_tests @@ -22,23 +22,31 @@ # Run the acl tests. $srcdir is set by the Makefile. source ./test_env.sh DATA_DIR=`pwd`/data_dir +DATA_DIRI=`pwd`/data_diri +DATA_DIRU=`pwd`/data_diru trap stop_brokers INT TERM QUIT start_brokers() { - ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no > qpidd.port + ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no --log-to-file local.log > qpidd.port LOCAL_PORT=`cat qpidd.port` + ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRI --load-module $ACL_LIB --acl-file policy.acl --auth no --acl-max-connect-per-ip 2 --log-to-file locali.log > qpiddi.port + LOCAL_PORTI=`cat qpiddi.port` + ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRU --load-module $ACL_LIB --acl-file policy.acl --auth no --acl-max-connect-per-user 2 --log-to-file localu.log > qpiddu.port + LOCAL_PORTU=`cat qpiddu.port` } stop_brokers() { $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT + $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTI + $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTU } test_loading_acl_from_absolute_path(){ POLICY_FILE=$srcdir/policy.acl rm -f temp.log PORT=`../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module $ACL_LIB --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null` - ACL_FILE=`grep "notice Read ACL file" temp.log | sed 's/^.*Read ACL file //'` + ACL_FILE=`grep "notice ACL: Read file" temp.log | sed 's/^.*Read file //'` $QPIDD_EXEC --no-module-dir -q --port $PORT if test "$ACL_FILE" != "\"$POLICY_FILE\""; then echo "unable to load policy file from an absolute path"; @@ -49,14 +57,22 @@ test_loading_acl_from_absolute_path(){ if test -d ${PYTHON_DIR} ; then rm -rf $DATA_DIR + rm -rf $DATA_DIRI + rm -rf $DATA_DIRU mkdir -p $DATA_DIR + mkdir -p $DATA_DIRI + mkdir -p $DATA_DIRU cp $srcdir/policy.acl $DATA_DIR + cp $srcdir/policy.acl $DATA_DIRI + cp $srcdir/policy.acl $DATA_DIRU start_brokers - echo "Running acl tests using brokers on ports $LOCAL_PORT" - $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl || EXITCODE=1 + echo "Running acl tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, and $LOCAL_PORTU" + $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl -Dport-i=$LOCAL_PORTI -Dport-u=$LOCAL_PORTU || EXITCODE=1 stop_brokers || EXITCODE=1 test_loading_acl_from_absolute_path || EXITCODE=1 rm -rf $DATA_DIR + rm -rf $DATA_DIRI + rm -rf $DATA_DIRU exit $EXITCODE fi diff --git a/cpp/src/tests/run_acl_tests.ps1 b/cpp/src/tests/run_acl_tests.ps1 index 46e070477f..8279d87e54 100644 --- a/cpp/src/tests/run_acl_tests.ps1 +++ b/cpp/src/tests/run_acl_tests.ps1 @@ -20,15 +20,12 @@ # Run the acl tests. $srcdir = Split-Path $myInvocation.InvocationName -$PYTHON_DIR = "$srcdir\..\..\..\python" +. .\test_env.ps1 if (!(Test-Path $PYTHON_DIR -pathType Container)) { "Skipping acl tests as python libs not found" exit 1 } -$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py" -$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py" -$env:PYTHONPATH="$PYTHON_DIR;$srcdir;$PYTHON_TEST_DIR;$QMF_LIB" $Global:BROKER_EXE = "" Function start_broker($acl_options) diff --git a/cpp/src/tests/run_cli_tests b/cpp/src/tests/run_cli_tests index ec5c71b646..ea44437c0d 100755 --- a/cpp/src/tests/run_cli_tests +++ b/cpp/src/tests/run_cli_tests @@ -56,9 +56,9 @@ start_brokers() { targs="--ignore=*xml*" fi - ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no $xargs > qpidd.port + ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --mgmt-publish no --auth no $xargs > qpidd.port LOCAL_PORT=`cat qpidd.port` - ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no $xargs > qpidd.port + ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --mgmt-publish no --auth no $xargs > qpidd.port REMOTE_PORT=`cat qpidd.port` } diff --git a/cpp/src/tests/run_cluster_authentication_soak b/cpp/src/tests/run_cluster_authentication_soak index 7bc406c4ca..24befa28ba 100755 --- a/cpp/src/tests/run_cluster_authentication_soak +++ b/cpp/src/tests/run_cluster_authentication_soak @@ -19,8 +19,9 @@ source ./test_env.sh -source $srcdir/ais_check source sasl_test_setup.sh +source cpg_check.sh +cpg_enabled || exit 0 with_ais_group ./cluster_authentication_soak 500 diff --git a/cpp/src/tests/run_cluster_authentication_test b/cpp/src/tests/run_cluster_authentication_test index 647200b869..844807a857 100755 --- a/cpp/src/tests/run_cluster_authentication_test +++ b/cpp/src/tests/run_cluster_authentication_test @@ -19,8 +19,9 @@ source ./test_env.sh -source $srcdir/ais_check source sasl_test_setup.sh +source cpg_check.sh +cpg_enabled || exit 0 with_ais_group ./cluster_authentication_soak diff --git a/cpp/src/tests/run_cluster_test b/cpp/src/tests/run_cluster_test index c022eea1fe..11df3d63a3 100755 --- a/cpp/src/tests/run_cluster_test +++ b/cpp/src/tests/run_cluster_test @@ -22,5 +22,6 @@ # Run the tests srcdir=`dirname $0` -. $srcdir/ais_check +source cpg_check.sh +cpg_enabled || exit 0 with_ais_group $srcdir/run_test ./cluster_test diff --git a/cpp/src/tests/run_cluster_tests b/cpp/src/tests/run_cluster_tests index e136d3810a..a5cea5ff6e 100755 --- a/cpp/src/tests/run_cluster_tests +++ b/cpp/src/tests/run_cluster_tests @@ -20,7 +20,9 @@ # source ./test_env.sh -source $srcdir/ais_check +source cpg_check.sh +cpg_enabled || exit 0 + test -x $QPID_PYTHON_TEST || { echo Skipping test, $QPID_PYTHON_TEST not found; exit 0; } diff --git a/cpp/src/tests/run_failover_soak b/cpp/src/tests/run_failover_soak index 4c2e8cc188..2c56bf7d6b 100755 --- a/cpp/src/tests/run_failover_soak +++ b/cpp/src/tests/run_failover_soak @@ -20,7 +20,8 @@ # source ./test_env.sh -. $srcdir/ais_check +source cpg_check.sh +cpg_enabled || exit 0 host=127.0.0.1 diff --git a/cpp/src/tests/run_federation_sys_tests b/cpp/src/tests/run_federation_sys_tests index f5f772d72e..76da176914 100755 --- a/cpp/src/tests/run_federation_sys_tests +++ b/cpp/src/tests/run_federation_sys_tests @@ -26,12 +26,8 @@ source ./test_env.sh MODULENAME=federation_sys # Test for clustering -ps -u root | grep 'aisexec\|corosync' > /dev/null -if (( $? == 0 )); then - CLUSTERING_ENABLED=1 -else - echo "WARNING: No clustering detected; tests using it will be ignored." -fi +source cpg_check.sh +if cpg_enabled; then CLUSTERING_ENABLED=1; fi # Test for long test if [[ "$1" == "LONG_TEST" ]]; then diff --git a/cpp/src/tests/run_federation_tests b/cpp/src/tests/run_federation_tests index b71fa14c47..7735b559cf 100755 --- a/cpp/src/tests/run_federation_tests +++ b/cpp/src/tests/run_federation_tests @@ -33,16 +33,13 @@ else SKIPTESTS='-i *_xml' # note: single quotes prevent expansion of * fi +QPIDD_CMD="../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no --log-enable=info+ --log-enable=debug+:Bridge --log-to-file" start_brokers() { - ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port - LOCAL_PORT=`cat qpidd.port` - ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port - REMOTE_PORT=`cat qpidd.port` - - ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port - REMOTE_B1=`cat qpidd.port` - ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port - REMOTE_B2=`cat qpidd.port` + rm -f fed_local.log fed_remote.log fed_b1.log fed_b2.log + LOCAL_PORT=$($QPIDD_CMD fed_local.log) + REMOTE_PORT=$($QPIDD_CMD fed_remote.log) + REMOTE_B1=$($QPIDD_CMD fed_b1.log) + REMOTE_B2=$($QPIDD_CMD fed_b2.log) } stop_brokers() { diff --git a/cpp/src/tests/run_federation_tests.ps1 b/cpp/src/tests/run_federation_tests.ps1 index 35353a870f..803b3eef6f 100644 --- a/cpp/src/tests/run_federation_tests.ps1 +++ b/cpp/src/tests/run_federation_tests.ps1 @@ -26,8 +26,7 @@ if (!(Test-Path $PYTHON_DIR -pathType Container)) { exit 1 } -$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py" -$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py" +. .\test_env.ps1 # Test runs from the tests directory but the broker executable is one level # up, and most likely in a subdirectory from there based on what build type. diff --git a/cpp/src/tests/run_header_test.ps1 b/cpp/src/tests/run_header_test.ps1 index 7d3e43a30f..344fac9cf9 100644 --- a/cpp/src/tests/run_header_test.ps1 +++ b/cpp/src/tests/run_header_test.ps1 @@ -28,6 +28,8 @@ if (!(Test-Path $PYTHON_DIR -pathType Container)) { exit 0 } +. .\test_env.ps1 + if (Test-Path qpidd.port) { set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1) } @@ -42,6 +44,5 @@ if (!(Test-Path $prog)) { } Invoke-Expression "$prog -p $env:QPID_PORT" | Write-Output -$env:PYTHONPATH="$PYTHON_DIR;$env:PYTHONPATH" Invoke-Expression "python $srcdir/header_test.py localhost $env:QPID_PORT" | Write-Output exit $LASTEXITCODE diff --git a/cpp/src/tests/run_msg_group_tests b/cpp/src/tests/run_msg_group_tests index 5a6da546f3..4e82759866 100755 --- a/cpp/src/tests/run_msg_group_tests +++ b/cpp/src/tests/run_msg_group_tests @@ -44,19 +44,19 @@ run_test() { declare -i i=0 declare -a tests -tests=("qpid-config -a $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups" +tests=("qpid-config -b $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 3" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 7 --randomize-group-size" - "qpid-config -a $BROKER_URL add queue ${QUEUE_NAME}-two --group-header=${GROUP_KEY} --shared-groups" + "qpid-config -b $BROKER_URL add queue ${QUEUE_NAME}-two --group-header=${GROUP_KEY} --shared-groups" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 3 --randomize-group-size" "msg_group_test -b $BROKER_URL -a ${QUEUE_NAME}-two --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 5" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 5 --receivers 2 --senders 3 --capacity 1 --ack-frequency 3 --randomize-group-size" - "qpid-config -a $BROKER_URL del queue ${QUEUE_NAME}-two --force" + "qpid-config -b $BROKER_URL del queue ${QUEUE_NAME}-two --force" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 3 --receivers 2 --senders 3 --capacity 1 --ack-frequency 1 --randomize-group-size" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 211 --group-size 13 --receivers 2 --senders 3 --capacity 47 --ack-frequency 79 --interleave 53" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10000 --group-size 1 --receivers 0 --senders 1" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10000 --receivers 5 --senders 0" - "qpid-config -a $BROKER_URL del queue $QUEUE_NAME --force") + "qpid-config -b $BROKER_URL del queue $QUEUE_NAME --force") while [ -n "${tests[i]}" ]; do run_test ${tests[i]} diff --git a/cpp/src/tests/run_msg_group_tests_soak b/cpp/src/tests/run_msg_group_tests_soak index 44995423cc..2ebbaf4efd 100755 --- a/cpp/src/tests/run_msg_group_tests_soak +++ b/cpp/src/tests/run_msg_group_tests_soak @@ -44,13 +44,13 @@ run_test() { declare -i i=0 declare -a tests -tests=("qpid-config -a $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups" +tests=("qpid-config -b $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 47 --ack-frequency 97" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 79 --ack-frequency 79" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 97 --ack-frequency 47" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 40000 --receivers 0 --senders 5 --group-size 13 --randomize-group-size" "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 200000 --receivers 3 --senders 0 --capacity 23 --ack-frequency 7" - "qpid-config -a $BROKER_URL del queue $QUEUE_NAME --force") + "qpid-config -b $BROKER_URL del queue $QUEUE_NAME --force") while [ -n "${tests[i]}" ]; do run_test ${tests[i]} diff --git a/cpp/src/tests/run_store_tests.ps1 b/cpp/src/tests/run_store_tests.ps1 index b2f0b1ccd8..0683892393 100644 --- a/cpp/src/tests/run_store_tests.ps1 +++ b/cpp/src/tests/run_store_tests.ps1 @@ -30,14 +30,14 @@ if ($test_store -ne "MSSQL" -and $test_store -ne "MSSQL-CLFS") { } $srcdir = Split-Path $myInvocation.InvocationName -$PYTHON_DIR = "$srcdir\..\..\..\python" + +. .\test_env.ps1 + if (!(Test-Path $PYTHON_DIR -pathType Container)) { "Skipping store tests as python libs not found" exit 1 } -$QMF_LIB = "$srcdir\..\..\..\extras\qmf\src\py" - # Test runs from the tests directory but the broker executable is one level # up, and most likely in a subdirectory from there based on what build type. # Look around for it before trying to start it. @@ -97,7 +97,7 @@ set-item -path env:QPID_PORT -value (get-content -path qpidd-store.port -totalco Remove-Item qpidd-store.port $PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py\qpid_tests\broker_0_10" -$env:PYTHONPATH="$PYTHON_DIR;$PYTHON_TEST_DIR;$env:PYTHONPATH;$QMF_LIB" +$env:PYTHONPATH="$PYTHON_TEST_DIR;$srcdir;$env:PYTHONPATH" python $PYTHON_DIR/qpid-python-test -m dtx -m persistence -b localhost:$env:QPID_PORT $fails $tests $RETCODE=$LASTEXITCODE if ($RETCODE -ne 0) { @@ -111,7 +111,6 @@ Invoke-Expression "$prog --quit --port $env:QPID_PORT" | Write-Output # Test 2... store.py starts/stops/restarts its own brokers $tests = "*" -$env:PYTHONPATH="$PYTHON_DIR;$QMF_LIB;$srcdir" $env:QPIDD_EXEC="$prog" $env:STORE_LIB="$store_dir\store$suffix.dll" if ($test_store -eq "MSSQL") { diff --git a/cpp/src/tests/run_test.ps1 b/cpp/src/tests/run_test.ps1 index ca990bc057..872e1dddb1 100644 --- a/cpp/src/tests/run_test.ps1 +++ b/cpp/src/tests/run_test.ps1 @@ -20,8 +20,7 @@ $srcdir = Split-Path $myInvocation.InvocationName # Set up environment and run a test executable or script. -$env:QPID_DATA_DIR = "" -$env:BOOST_TEST_SHOW_PROGRESS = "yes" +. .\test_env.ps1 # The test exe is probably not in the current binary dir - it's usually # placed in a subdirectory based on the configuration built in Visual Studio. @@ -30,7 +29,7 @@ $env:BOOST_TEST_SHOW_PROGRESS = "yes" # one level up. $prog = $args[0] $is_script = $prog -match ".ps1$" -if (!$is_script) { +if (!$is_script -and !(Test-Path "$prog")) { . $srcdir\find_prog.ps1 $prog $args[0] = $prog $env:QPID_LIB_DIR = "..\$sub" diff --git a/cpp/src/tests/sasl.mk b/cpp/src/tests/sasl.mk index 69b24c3f8a..11731dcf40 100644 --- a/cpp/src/tests/sasl.mk +++ b/cpp/src/tests/sasl.mk @@ -20,18 +20,37 @@ # Test that are only relevant if SASL is enabled. if HAVE_SASL +if HAVE_LIBCPG check_PROGRAMS+=cluster_authentication_soak cluster_authentication_soak_INCLUDES=$(PUBLIC_INCLUDES) cluster_authentication_soak_SOURCES=cluster_authentication_soak.cpp ForkedBroker.h ForkedBroker.cpp cluster_authentication_soak_LDADD=$(lib_client) $(lib_broker) +endif HAVE_LIBCPG # Note: sasl_version is not a test -- it is a tool used by tests. check_PROGRAMS+=sasl_version sasl_version_SOURCES=sasl_version.cpp sasl_version_LDADD=$(lib_client) -TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex_dynamic sasl_fed_ex_link sasl_fed_ex_queue sasl_fed_ex_route sasl_fed_ex_route_cluster sasl_fed_ex_link_cluster sasl_fed_ex_queue_cluster sasl_fed_ex_dynamic_cluster sasl_no_dir +TESTS += sasl_fed + sasl_fed_ex_dynamic + sasl_fed_ex_link + sasl_fed_ex_queue + sasl_fed_ex_route + sasl_no_dir + +if HAVE_LIBCPG + +TESTS += run_cluster_authentication_test \ + sasl_fed_ex_route_cluster \ + sasl_fed_ex_link_cluster \ + sasl_fed_ex_queue_cluster \ + sasl_fed_ex_dynamic_cluster + LONG_TESTS += run_cluster_authentication_soak + +endif HAVE_LIBCPG + EXTRA_DIST += run_cluster_authentication_test \ sasl_fed \ sasl_fed_ex \ diff --git a/cpp/src/tests/sasl_fed b/cpp/src/tests/sasl_fed index 884c44177c..9dc2dd46e2 100755 --- a/cpp/src/tests/sasl_fed +++ b/cpp/src/tests/sasl_fed @@ -90,23 +90,23 @@ EXCHANGE_NAME=sasl_fedex #-------------------------------------------------- #echo " add exchanges" #-------------------------------------------------- -$QPID_CONFIG_EXEC -a localhost:$broker_1_port add exchange direct $EXCHANGE_NAME -$QPID_CONFIG_EXEC -a localhost:$broker_2_port add exchange direct $EXCHANGE_NAME +$QPID_CONFIG_EXEC -b localhost:$broker_1_port add exchange direct $EXCHANGE_NAME +$QPID_CONFIG_EXEC -b localhost:$broker_2_port add exchange direct $EXCHANGE_NAME #-------------------------------------------------- #echo " add queues" #-------------------------------------------------- -$QPID_CONFIG_EXEC -a localhost:$broker_1_port add queue $QUEUE_NAME -$QPID_CONFIG_EXEC -a localhost:$broker_2_port add queue $QUEUE_NAME +$QPID_CONFIG_EXEC -b localhost:$broker_1_port add queue $QUEUE_NAME +$QPID_CONFIG_EXEC -b localhost:$broker_2_port add queue $QUEUE_NAME sleep 5 #-------------------------------------------------- #echo " create bindings" #-------------------------------------------------- -$QPID_CONFIG_EXEC -a localhost:$broker_1_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY -$QPID_CONFIG_EXEC -a localhost:$broker_2_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY +$QPID_CONFIG_EXEC -b localhost:$broker_1_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY +$QPID_CONFIG_EXEC -b localhost:$broker_2_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY sleep 5 @@ -130,13 +130,13 @@ sleep 5 #-------------------------------------------------- #echo " Examine Broker $broker_1_port" #-------------------------------------------------- -broker_1_message_count=`$PYTHON_COMMANDS/qpid-stat -q localhost:$broker_1_port | grep sasl_fed_queue | awk '{print $2}'` +broker_1_message_count=`$PYTHON_COMMANDS/qpid-stat -q -b localhost:$broker_1_port | grep sasl_fed_queue | awk '{print $2}'` #echo " " #-------------------------------------------------- #echo " Examine Broker $broker_2_port" #-------------------------------------------------- -broker_2_message_count=`$PYTHON_COMMANDS/qpid-stat -q localhost:$broker_2_port | grep sasl_fed_queue | awk '{print $2}'` +broker_2_message_count=`$PYTHON_COMMANDS/qpid-stat -q -b localhost:$broker_2_port | grep sasl_fed_queue | awk '{print $2}'` #echo " " #-------------------------------------------------- diff --git a/cpp/src/tests/sasl_fed_ex b/cpp/src/tests/sasl_fed_ex index 716a806874..cc5b310067 100755 --- a/cpp/src/tests/sasl_fed_ex +++ b/cpp/src/tests/sasl_fed_ex @@ -280,18 +280,18 @@ EXCHANGE_NAME=sasl_fedex print "add exchanges" -$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add exchange direct $EXCHANGE_NAME -$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add exchange direct $EXCHANGE_NAME +$QPID_CONFIG_EXEC -b localhost:${SRC_TCP_PORT} add exchange direct $EXCHANGE_NAME +$QPID_CONFIG_EXEC -b localhost:${DST_TCP_PORT} add exchange direct $EXCHANGE_NAME print "add queues" -$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add queue $QUEUE_NAME -$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add queue $QUEUE_NAME +$QPID_CONFIG_EXEC -b localhost:${SRC_TCP_PORT} add queue $QUEUE_NAME +$QPID_CONFIG_EXEC -b localhost:${DST_TCP_PORT} add queue $QUEUE_NAME print "create bindings" -$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY -$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY +$QPID_CONFIG_EXEC -b localhost:${SRC_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY +$QPID_CONFIG_EXEC -b localhost:${DST_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY # diff --git a/cpp/src/tests/sasl_fed_ex_dynamic_cluster b/cpp/src/tests/sasl_fed_ex_dynamic_cluster index b0cceccecb..fd6b72a4f2 100755 --- a/cpp/src/tests/sasl_fed_ex_dynamic_cluster +++ b/cpp/src/tests/sasl_fed_ex_dynamic_cluster @@ -21,7 +21,9 @@ source ./test_env.sh -source $srcdir/ais_check +source cpg_check.sh +cpg_enabled || exit 0 + with_ais_group ${srcdir}/sasl_fed_ex dynamic cluster diff --git a/cpp/src/tests/sasl_fed_ex_link_cluster b/cpp/src/tests/sasl_fed_ex_link_cluster index 4139300b12..34b2aa4a5f 100755 --- a/cpp/src/tests/sasl_fed_ex_link_cluster +++ b/cpp/src/tests/sasl_fed_ex_link_cluster @@ -21,7 +21,8 @@ source ./test_env.sh -source $srcdir/ais_check +source cpg_check.sh +cpg_enabled || exit 0 with_ais_group ${srcdir}/sasl_fed_ex link cluster diff --git a/cpp/src/tests/sasl_fed_ex_queue_cluster b/cpp/src/tests/sasl_fed_ex_queue_cluster index f251420e08..14f36f6fc4 100755 --- a/cpp/src/tests/sasl_fed_ex_queue_cluster +++ b/cpp/src/tests/sasl_fed_ex_queue_cluster @@ -21,7 +21,8 @@ source ./test_env.sh -source ${srcdir}/ais_check +source cpg_check.sh +cpg_enabled || exit 0 with_ais_group ${srcdir}/sasl_fed_ex queue cluster diff --git a/cpp/src/tests/sasl_fed_ex_route_cluster b/cpp/src/tests/sasl_fed_ex_route_cluster index a5d1542def..756476056e 100755 --- a/cpp/src/tests/sasl_fed_ex_route_cluster +++ b/cpp/src/tests/sasl_fed_ex_route_cluster @@ -21,7 +21,8 @@ source ./test_env.sh -source ${srcdir}/ais_check +source cpg_check.sh +cpg_enabled || exit 0 with_ais_group ${srcdir}/sasl_fed_ex route cluster diff --git a/cpp/src/tests/ssl_test b/cpp/src/tests/ssl_test index 4dbb7df392..91ff8eec1e 100755 --- a/cpp/src/tests/ssl_test +++ b/cpp/src/tests/ssl_test @@ -100,8 +100,10 @@ start_ssl_mux_broker() { PORTS=( ${PORTS[@]} $1 ) } +sasl_config_dir=$builddir/sasl_config + start_authenticating_broker() { - start_brokers 1 "--transport ssl --ssl-port 0 --require-encryption --ssl-sasl-no-dict --ssl-require-client-authentication --auth yes" + start_brokers 1 "--transport ssl --ssl-port 0 --require-encryption --ssl-sasl-no-dict --ssl-require-client-authentication --auth yes --sasl-config=${sasl_config_dir}" } ssl_cluster_broker() { # $1 = port @@ -140,6 +142,12 @@ URL=amqp:ssl:$TEST_HOSTNAME:$PORT MSG=`./qpid-receive -b $URL -a "foo;{create:always}" --messages 1` test "$MSG" = "hello" || { echo "receive failed '$MSG' != 'hello'"; exit 1; } +## Test connection with a combination of URL and connection options (in messaging API) +URL=$TEST_HOSTNAME:$PORT +./qpid-send -b $URL --connection-options '{transport:ssl,heartbeat:2}' --content-string='hello again' -a "foo;{create:always}" +MSG=`./qpid-receive -b $URL --connection-options '{transport:ssl,heartbeat:2}' -a "foo;{create:always}" --messages 1` +test "$MSG" = "hello again" || { echo "receive failed '$MSG' != 'hello again'"; exit 1; } + #### Client Authentication tests start_authenticating_broker @@ -184,7 +192,8 @@ stop_brokers test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported. ## Test failover in a cluster using SSL only -. $srcdir/ais_check # Will exit if clustering not enabled. +source cpg_check.sh +cpg_enabled || exit 0 PORT1=`pick_port`; ssl_cluster_broker $PORT1 echo "Running SSL cluster broker on port $PORT1" diff --git a/cpp/src/tests/start_cluster b/cpp/src/tests/start_cluster index 84f98b3b2d..78fd104d9c 100755 --- a/cpp/src/tests/start_cluster +++ b/cpp/src/tests/start_cluster @@ -24,7 +24,8 @@ # Execute command with the ais group set. source ./test_env.sh -. `dirname $0`/ais_check +source cpg_check.sh +cpg_enabled || exit 0 rm -f cluster*.log cluster.ports qpidd.port diff --git a/cpp/src/tests/test_env.ps1.in b/cpp/src/tests/test_env.ps1.in new file mode 100644 index 0000000000..60ba4305a5 --- /dev/null +++ b/cpp/src/tests/test_env.ps1.in @@ -0,0 +1,78 @@ +# +# 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. +# + +# Environment variables substituted by configure/cmake. +$srcdir="@abs_srcdir@" +$builddir="@abs_builddir@" +$top_srcdir="@abs_top_srcdir@" +$top_builddir="@abs_top_builddir@" +$moduledir="$top_builddir\src@builddir_lib_suffix@" +$testmoduledir="$builddir@builddir_lib_suffix@" + +# Python paths and directories +$PYTHON_DIR="$builddir\python" +$QPID_PYTHON_TEST="$PYTHON_DIR\commands\qpid-python-test" +if ( !(Test-Path "$PYTHON_DIR") -and (Test-Path "$top_srcdir\..\python")) { + $PYTHON_DIR="$top_srcdir\..\python" + $QPID_PYTHON_TEST="$PYTHON_DIR\qpid-python-test" +} +$QPID_TESTS="$top_srcdir\..\tests" +$QPID_TESTS_PY="$QPID_TESTS\src\py" +$QPID_TOOLS="$top_srcdir\..\tools" +$QPID_TOOLS_LIBS="$QPID_TOOLS\src\py" +$QMF_LIB="$top_srcdir\..\extras\qmf\src\py" +$PYTHON_COMMANDS="$QPID_TOOLS\src\py" +$env:PYTHONPATH="$srcdir;$PYTHON_DIR;$PYTHON_COMMANDS;$QPID_TESTS_PY;$QPID_TOOLS_LIBS;$QMF_LIB;$env:PYTHONPATH" +$QPID_CONFIG_EXEC="$PYTHON_COMMANDS\qpid-config" +$QPID_ROUTE_EXEC="$PYTHON_COMMANDS\qpid-route" +$QPID_CLUSTER_EXEC="$PYTHON_COMMANDS\qpid-cluster" +$QPID_HA_TOOL_EXEC="$PYTHON_COMMANDS\qpid-ha-tool" + +# Executables +$env:QPIDD_EXEC="$top_builddir\src\qpidd" +$env:QPID_WATCHDOG_EXEC="$top_builddir\src\qpidd_watchdog" + +# Test executables +$QPID_TEST_EXEC_DIR="$builddir" +$RECEIVER_EXEC="$QPID_TEST_EXEC_DIR\receiver" +$SENDER_EXEC="$QPID_TEST_EXEC_DIR\sender" + +# Path +$env:PATH="$top_builddir\src;$builddir;$srcdir;$PYTHON_COMMANDS;$QPID_TEST_EXEC_DIR;$env:PATH" + +# Modules +$env:TEST_STORE_LIB="$testmoduledir\test_store.so" + +#exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; } +#exportmodule ACL_LIB acl.so +#exportmodule CLUSTER_LIB cluster.so +#exportmodule REPLICATING_LISTENER_LIB replicating_listener.so +#exportmodule REPLICATION_EXCHANGE_LIB replication_exchange.so +#exportmodule SSLCONNECTOR_LIB sslconnector.so +#exportmodule SSL_LIB ssl.so +#exportmodule WATCHDOG_LIB watchdog.so +#exportmodule XML_LIB xml.so + +# Qpid options +$env:QPID_NO_MODULE_DIR="1" # Don't accidentally load installed modules +$env:QPID_DATA_DIR= # Default to no data dir, not ~/.qpidd + +# Options for boost test framework +$env:BOOST_TEST_SHOW_PROGRESS="yes" +$env:BOOST_TEST_CATCH_SYSTEM_ERRORS="no" diff --git a/cpp/src/tests/test_env.sh.in b/cpp/src/tests/test_env.sh.in index 0cd658bd80..cee36843ae 100644 --- a/cpp/src/tests/test_env.sh.in +++ b/cpp/src/tests/test_env.sh.in @@ -44,6 +44,7 @@ export PYTHONPATH=$srcdir:$PYTHON_DIR:$PYTHON_COMMANDS:$QPID_TESTS_PY:$QMF_LIB:$ export QPID_CONFIG_EXEC=$PYTHON_COMMANDS/qpid-config export QPID_ROUTE_EXEC=$PYTHON_COMMANDS/qpid-route export QPID_CLUSTER_EXEC=$PYTHON_COMMANDS/qpid-cluster +export QPID_HA_EXEC=$PYTHON_COMMANDS/qpid-ha # Executables export QPIDD_EXEC=$top_builddir/src/qpidd @@ -74,6 +75,7 @@ exportmodule XML_LIB xml.so # Qpid options export QPID_NO_MODULE_DIR=1 # Don't accidentally load installed modules export QPID_DATA_DIR= +export QPID_CONFIG=$srcdir/qpidd-empty.conf # Use temporary directory if $HOME does not exist if [ ! -e "$HOME" ]; then diff --git a/cpp/src/tests/testagent.cpp b/cpp/src/tests/testagent.cpp index 98520b424a..e6010a8e00 100644 --- a/cpp/src/tests/testagent.cpp +++ b/cpp/src/tests/testagent.cpp @@ -36,9 +36,12 @@ #include <sstream> +namespace qpid { +namespace tests { + static bool running = true; -using namespace std; +using std::string; using qpid::management::ManagementAgent; using qpid::management::ManagementObject; using qpid::management::Manageable; @@ -191,12 +194,14 @@ int main_int(int argc, char** argv) return 0; } +}} // namespace qpid::tests + int main(int argc, char** argv) { try { - return main_int(argc, argv); + return qpid::tests::main_int(argc, argv); } catch(std::exception& e) { - cerr << "Top Level Exception: " << e.what() << endl; + std::cerr << "Top Level Exception: " << e.what() << std::endl; return 1; } } diff --git a/cpp/src/tests/testlib.py b/cpp/src/tests/testlib.py index fe57a84a81..71ad59e5c1 100644 --- a/cpp/src/tests/testlib.py +++ b/cpp/src/tests/testlib.py @@ -348,8 +348,8 @@ class TestBaseCluster(TestBase): def _qpidConfig(self, nodeNumber, clusterName, action): """Configure some aspect of a qpid broker using the qpid_config executable""" port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT] - #print "%s -a localhost:%d %s" % (self._qpidConfigExec, port, action) - ret = os.spawnl(os.P_WAIT, self._qpidConfigExec, self._qpidConfigExec, "-a", "localhost:%d" % port, *action.split()) + #print "%s -b localhost:%d %s" % (self._qpidConfigExec, port, action) + ret = os.spawnl(os.P_WAIT, self._qpidConfigExec, self._qpidConfigExec, "-b", "localhost:%d" % port, *action.split()) if ret != 0: raise Exception("_qpidConfig(): cluster=\"%s\" nodeNumber=%d port=%d action=\"%s\" returned %d" % \ (clusterName, nodeNumber, port, action, ret)) diff --git a/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp index 024f20b147..14f1e46606 100644 --- a/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp +++ b/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp @@ -32,7 +32,9 @@ #include <windows.h> #include <iostream> -namespace { +namespace qpid { +namespace tests { +namespace windows { // Instead of popping up a window for exceptions, just print something out LONG _stdcall UnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo) @@ -73,4 +75,4 @@ redirect_errors_to_stderr::redirect_errors_to_stderr() SetUnhandledExceptionFilter (&UnhandledExceptionFilter); } -} // namespace +}}} // namespace diff --git a/cpp/src/windows/QpiddBroker.cpp b/cpp/src/windows/QpiddBroker.cpp index 42ba97bdb1..89a8945d00 100644 --- a/cpp/src/windows/QpiddBroker.cpp +++ b/cpp/src/windows/QpiddBroker.cpp @@ -32,11 +32,12 @@ #include <iostream> #include <windows.h> -using namespace qpid::broker; +namespace qpid { +namespace broker { BootstrapOptions::BootstrapOptions(const char* argv0) : qpid::Options("Options"), - common("", QPIDD_CONF_FILE), + common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE), module(QPIDD_MODULE_DIR), log(argv0) { @@ -314,7 +315,7 @@ struct QpiddWindowsOptions : public QpiddOptionsPrivate { QpiddOptions::QpiddOptions(const char* argv0) : qpid::Options("Options"), - common("", QPIDD_CONF_FILE), + common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE), module(QPIDD_MODULE_DIR), log(argv0) { @@ -451,6 +452,7 @@ int QpiddBroker::execute (QpiddOptions *options) { return 0; } +}} // namespace qpid::broker int main(int argc, char* argv[]) { @@ -459,13 +461,13 @@ int main(int argc, char* argv[]) // the service is stopped. SERVICE_TABLE_ENTRY dispatchTable[] = { - { "", (LPSERVICE_MAIN_FUNCTION)ServiceMain }, + { "", (LPSERVICE_MAIN_FUNCTION)qpid::broker::ServiceMain }, { NULL, NULL } }; if (!StartServiceCtrlDispatcher(dispatchTable)) { DWORD err = ::GetLastError(); if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) // Run as console - return run_broker(argc, argv); + return qpid::broker::run_broker(argc, argv); throw QPID_WINDOWS_ERROR(err); } return 0; diff --git a/cpp/src/windows/SCM.cpp b/cpp/src/windows/SCM.cpp index 232bb04c17..2eeb143427 100644 --- a/cpp/src/windows/SCM.cpp +++ b/cpp/src/windows/SCM.cpp @@ -1,332 +1,332 @@ -/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "qpid/log/Statement.h"
-#include "qpid/sys/windows/check.h"
-#include "SCM.h"
-
-#pragma comment(lib, "advapi32.lib")
-
-namespace {
-
-// Container that will close a SC_HANDLE upon destruction.
-class AutoServiceHandle {
-public:
- AutoServiceHandle(SC_HANDLE h_ = NULL) : h(h_) {}
- ~AutoServiceHandle() { if (h != NULL) ::CloseServiceHandle(h); }
- void release() { h = NULL; }
- void reset(SC_HANDLE newHandle)
- {
- if (h != NULL)
- ::CloseServiceHandle(h);
- h = newHandle;
- }
- operator SC_HANDLE() const { return h; }
-
-private:
- SC_HANDLE h;
-};
-
-}
-
-namespace qpid {
-namespace windows {
-
-SCM::SCM() : scmHandle(NULL)
-{
-}
-
-SCM::~SCM()
-{
- if (NULL != scmHandle)
- ::CloseServiceHandle(scmHandle);
-}
-
-/**
- * Install this executable as a service
- */
-void SCM::install(const string& serviceName,
- const string& serviceDesc,
- const string& args,
- DWORD startType,
- const string& account,
- const string& password,
- const string& depends)
-{
- // Handle dependent service name list; Windows wants a set of nul-separated
- // names ending with a double nul.
- string depends2 = depends;
- if (!depends2.empty()) {
- // CDL to null delimiter w/ trailing double null
- size_t p = 0;
- while ((p = depends2.find_first_of( ',', p)) != string::npos)
- depends2.replace(p, 1, 1, '\0');
- depends2.push_back('\0');
- depends2.push_back('\0');
- }
-
-#if 0
- // I'm nervous about adding a user/password check here. Is this a
- // potential attack vector, letting users check passwords without
- // control? -Steve Huston, Feb 24, 2011
-
- // Validate account, password
- HANDLE hToken = NULL;
- bool logStatus = false;
- if (!account.empty() && !password.empty() &&
- !(logStatus = ::LogonUserA(account.c_str(),
- "",
- password.c_str(),
- LOGON32_LOGON_NETWORK,
- LOGON32_PROVIDER_DEFAULT,
- &hToken ) != 0))
- std::cout << "warning: supplied account & password failed with LogonUser." << std::endl;
- if (logStatus)
- ::CloseHandle(hToken);
-#endif
-
- // Get fully qualified .exe name
- char myPath[MAX_PATH];
- DWORD myPathLength = ::GetModuleFileName(NULL, myPath, MAX_PATH);
- QPID_WINDOWS_CHECK_NOT(myPathLength, 0);
- string imagePath(myPath, myPathLength);
- if (!args.empty())
- imagePath += " " + args;
-
- // Ensure there's a handle to the SCM database.
- openSvcManager();
-
- // Create the service
- SC_HANDLE svcHandle;
- svcHandle = ::CreateService(scmHandle, // SCM database
- serviceName.c_str(), // name of service
- serviceDesc.c_str(), // name to display
- SERVICE_ALL_ACCESS, // desired access
- SERVICE_WIN32_OWN_PROCESS, // service type
- startType, // start type
- SERVICE_ERROR_NORMAL, // error cntrl type
- imagePath.c_str(), // path to service's binary w/ optional arguments
- NULL, // no load ordering group
- NULL, // no tag identifier
- depends2.empty() ? NULL : depends2.c_str(),
- account.empty() ? NULL : account.c_str(), // account name, or NULL for LocalSystem
- password.empty() ? NULL : password.c_str()); // password, or NULL for none
- QPID_WINDOWS_CHECK_NULL(svcHandle);
- ::CloseServiceHandle(svcHandle);
- QPID_LOG(info, "Service installed successfully");
-}
-
-/**
- *
- */
-void SCM::uninstall(const string& serviceName)
-{
- // Ensure there's a handle to the SCM database.
- openSvcManager();
- AutoServiceHandle svc(::OpenService(scmHandle,
- serviceName.c_str(),
- DELETE));
- QPID_WINDOWS_CHECK_NULL((SC_HANDLE)svc);
- QPID_WINDOWS_CHECK_NOT(::DeleteService(svc), 0);
- QPID_LOG(info, "Service deleted successfully.");
-}
-
-/**
- * Attempt to start the service.
- */
-void SCM::start(const string& serviceName)
-{
- // Ensure we have a handle to the SCM database.
- openSvcManager();
-
- // Get a handle to the service.
- AutoServiceHandle svc(::OpenService(scmHandle,
- serviceName.c_str(),
- SERVICE_ALL_ACCESS));
- QPID_WINDOWS_CHECK_NULL(svc);
-
- // Check the status in case the service is not stopped.
- DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
- if (state == SERVICE_STOP_PENDING)
- throw qpid::Exception("Timed out waiting for running service to stop.");
-
- // Attempt to start the service.
- QPID_WINDOWS_CHECK_NOT(::StartService(svc, 0, NULL), 0);
-
- QPID_LOG(info, "Service start pending...");
-
- // Check the status until the service is no longer start pending.
- state = waitForStateChangeFrom(svc, SERVICE_START_PENDING);
- // Determine whether the service is running.
- if (state == SERVICE_RUNNING) {
- QPID_LOG(info, "Service started successfully");
- }
- else {
- throw qpid::Exception(QPID_MSG("Service not yet running; state now " << state));
- }
-}
-
-/**
- *
- */
-void SCM::stop(const string& serviceName)
-{
- // Ensure a handle to the SCM database.
- openSvcManager();
-
- // Get a handle to the service.
- AutoServiceHandle svc(::OpenService(scmHandle,
- serviceName.c_str(),
- SERVICE_STOP | SERVICE_QUERY_STATUS |
- SERVICE_ENUMERATE_DEPENDENTS));
- QPID_WINDOWS_CHECK_NULL(svc);
-
- // Make sure the service is not already stopped; if it's stop-pending,
- // wait for it to finalize.
- DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
- if (state == SERVICE_STOPPED) {
- QPID_LOG(info, "Service is already stopped");
- return;
- }
-
- // If the service is running, dependencies must be stopped first.
- std::auto_ptr<ENUM_SERVICE_STATUS> deps;
- DWORD numDeps = getDependentServices(svc, deps);
- for (DWORD i = 0; i < numDeps; i++)
- stop(deps.get()[i].lpServiceName);
-
- // Dependents stopped; send a stop code to the service.
- SERVICE_STATUS_PROCESS ssp;
- if (!::ControlService(svc, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp))
- throw qpid::Exception(QPID_MSG("Stopping " << serviceName << ": " <<
- qpid::sys::strError(::GetLastError())));
-
- // Wait for the service to stop.
- state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
- if (state == SERVICE_STOPPED)
- QPID_LOG(info, QPID_MSG("Service " << serviceName <<
- " stopped successfully."));
-}
-
-/**
- *
- */
-void SCM::openSvcManager()
-{
- if (NULL != scmHandle)
- return;
-
- scmHandle = ::OpenSCManager(NULL, // local computer
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // Rights
- QPID_WINDOWS_CHECK_NULL(scmHandle);
-}
-
-DWORD SCM::waitForStateChangeFrom(SC_HANDLE svc, DWORD originalState)
-{
- SERVICE_STATUS_PROCESS ssStatus;
- DWORD bytesNeeded;
- DWORD waitTime;
- if (!::QueryServiceStatusEx(svc, // handle to service
- SC_STATUS_PROCESS_INFO, // information level
- (LPBYTE)&ssStatus, // address of structure
- sizeof(ssStatus), // size of structure
- &bytesNeeded)) // size needed if buffer is too small
- throw QPID_WINDOWS_ERROR(::GetLastError());
-
- // Save the tick count and initial checkpoint.
- DWORD startTickCount = ::GetTickCount();
- DWORD oldCheckPoint = ssStatus.dwCheckPoint;
-
- // Wait for the service to change out of the noted state.
- while (ssStatus.dwCurrentState == originalState) {
- // Do not wait longer than the wait hint. A good interval is
- // one-tenth of the wait hint but not less than 1 second
- // and not more than 10 seconds.
- waitTime = ssStatus.dwWaitHint / 10;
- if (waitTime < 1000)
- waitTime = 1000;
- else if (waitTime > 10000)
- waitTime = 10000;
-
- ::Sleep(waitTime);
-
- // Check the status until the service is no longer stop pending.
- if (!::QueryServiceStatusEx(svc,
- SC_STATUS_PROCESS_INFO,
- (LPBYTE) &ssStatus,
- sizeof(ssStatus),
- &bytesNeeded))
- throw QPID_WINDOWS_ERROR(::GetLastError());
-
- if (ssStatus.dwCheckPoint > oldCheckPoint) {
- // Continue to wait and check.
- startTickCount = ::GetTickCount();
- oldCheckPoint = ssStatus.dwCheckPoint;
- } else {
- if ((::GetTickCount() - startTickCount) > ssStatus.dwWaitHint)
- break;
- }
- }
- return ssStatus.dwCurrentState;
-}
-
-/**
- * Get the services that depend on @arg svc. All dependent service info
- * is returned in an array of ENUM_SERVICE_STATUS structures via @arg deps.
- *
- * @retval The number of dependent services.
- */
-DWORD SCM::getDependentServices(SC_HANDLE svc,
- std::auto_ptr<ENUM_SERVICE_STATUS>& deps)
-{
- DWORD bytesNeeded;
- DWORD numEntries;
-
- // Pass a zero-length buffer to get the required buffer size.
- if (::EnumDependentServices(svc,
- SERVICE_ACTIVE,
- 0,
- 0,
- &bytesNeeded,
- &numEntries)) {
- // If the Enum call succeeds, then there are no dependent
- // services, so do nothing.
- return 0;
- }
-
- if (::GetLastError() != ERROR_MORE_DATA)
- throw QPID_WINDOWS_ERROR((::GetLastError()));
-
- // Allocate a buffer for the dependencies.
- deps.reset((LPENUM_SERVICE_STATUS)(new char[bytesNeeded]));
- // Enumerate the dependencies.
- if (!::EnumDependentServices(svc,
- SERVICE_ACTIVE,
- deps.get(),
- bytesNeeded,
- &bytesNeeded,
- &numEntries))
- throw QPID_WINDOWS_ERROR((::GetLastError()));
- return numEntries;
-}
-
-} } // namespace qpid::windows
+/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/log/Statement.h" +#include "qpid/sys/windows/check.h" +#include "SCM.h" + +#pragma comment(lib, "advapi32.lib") + +namespace qpid { +namespace windows { + +namespace { + +// Container that will close a SC_HANDLE upon destruction. +class AutoServiceHandle { +public: + AutoServiceHandle(SC_HANDLE h_ = NULL) : h(h_) {} + ~AutoServiceHandle() { if (h != NULL) ::CloseServiceHandle(h); } + void release() { h = NULL; } + void reset(SC_HANDLE newHandle) + { + if (h != NULL) + ::CloseServiceHandle(h); + h = newHandle; + } + operator SC_HANDLE() const { return h; } + +private: + SC_HANDLE h; +}; + +} + +SCM::SCM() : scmHandle(NULL) +{ +} + +SCM::~SCM() +{ + if (NULL != scmHandle) + ::CloseServiceHandle(scmHandle); +} + +/** + * Install this executable as a service + */ +void SCM::install(const string& serviceName, + const string& serviceDesc, + const string& args, + DWORD startType, + const string& account, + const string& password, + const string& depends) +{ + // Handle dependent service name list; Windows wants a set of nul-separated + // names ending with a double nul. + string depends2 = depends; + if (!depends2.empty()) { + // CDL to null delimiter w/ trailing double null + size_t p = 0; + while ((p = depends2.find_first_of( ',', p)) != string::npos) + depends2.replace(p, 1, 1, '\0'); + depends2.push_back('\0'); + depends2.push_back('\0'); + } + +#if 0 + // I'm nervous about adding a user/password check here. Is this a + // potential attack vector, letting users check passwords without + // control? -Steve Huston, Feb 24, 2011 + + // Validate account, password + HANDLE hToken = NULL; + bool logStatus = false; + if (!account.empty() && !password.empty() && + !(logStatus = ::LogonUserA(account.c_str(), + "", + password.c_str(), + LOGON32_LOGON_NETWORK, + LOGON32_PROVIDER_DEFAULT, + &hToken ) != 0)) + std::cout << "warning: supplied account & password failed with LogonUser." << std::endl; + if (logStatus) + ::CloseHandle(hToken); +#endif + + // Get fully qualified .exe name + char myPath[MAX_PATH]; + DWORD myPathLength = ::GetModuleFileName(NULL, myPath, MAX_PATH); + QPID_WINDOWS_CHECK_NOT(myPathLength, 0); + string imagePath(myPath, myPathLength); + if (!args.empty()) + imagePath += " " + args; + + // Ensure there's a handle to the SCM database. + openSvcManager(); + + // Create the service + SC_HANDLE svcHandle; + svcHandle = ::CreateService(scmHandle, // SCM database + serviceName.c_str(), // name of service + serviceDesc.c_str(), // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + startType, // start type + SERVICE_ERROR_NORMAL, // error cntrl type + imagePath.c_str(), // path to service's binary w/ optional arguments + NULL, // no load ordering group + NULL, // no tag identifier + depends2.empty() ? NULL : depends2.c_str(), + account.empty() ? NULL : account.c_str(), // account name, or NULL for LocalSystem + password.empty() ? NULL : password.c_str()); // password, or NULL for none + QPID_WINDOWS_CHECK_NULL(svcHandle); + ::CloseServiceHandle(svcHandle); + QPID_LOG(info, "Service installed successfully"); +} + +/** + * + */ +void SCM::uninstall(const string& serviceName) +{ + // Ensure there's a handle to the SCM database. + openSvcManager(); + AutoServiceHandle svc(::OpenService(scmHandle, + serviceName.c_str(), + DELETE)); + QPID_WINDOWS_CHECK_NULL((SC_HANDLE)svc); + QPID_WINDOWS_CHECK_NOT(::DeleteService(svc), 0); + QPID_LOG(info, "Service deleted successfully."); +} + +/** + * Attempt to start the service. + */ +void SCM::start(const string& serviceName) +{ + // Ensure we have a handle to the SCM database. + openSvcManager(); + + // Get a handle to the service. + AutoServiceHandle svc(::OpenService(scmHandle, + serviceName.c_str(), + SERVICE_ALL_ACCESS)); + QPID_WINDOWS_CHECK_NULL(svc); + + // Check the status in case the service is not stopped. + DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING); + if (state == SERVICE_STOP_PENDING) + throw qpid::Exception("Timed out waiting for running service to stop."); + + // Attempt to start the service. + QPID_WINDOWS_CHECK_NOT(::StartService(svc, 0, NULL), 0); + + QPID_LOG(info, "Service start pending..."); + + // Check the status until the service is no longer start pending. + state = waitForStateChangeFrom(svc, SERVICE_START_PENDING); + // Determine whether the service is running. + if (state == SERVICE_RUNNING) { + QPID_LOG(info, "Service started successfully"); + } + else { + throw qpid::Exception(QPID_MSG("Service not yet running; state now " << state)); + } +} + +/** + * + */ +void SCM::stop(const string& serviceName) +{ + // Ensure a handle to the SCM database. + openSvcManager(); + + // Get a handle to the service. + AutoServiceHandle svc(::OpenService(scmHandle, + serviceName.c_str(), + SERVICE_STOP | SERVICE_QUERY_STATUS | + SERVICE_ENUMERATE_DEPENDENTS)); + QPID_WINDOWS_CHECK_NULL(svc); + + // Make sure the service is not already stopped; if it's stop-pending, + // wait for it to finalize. + DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING); + if (state == SERVICE_STOPPED) { + QPID_LOG(info, "Service is already stopped"); + return; + } + + // If the service is running, dependencies must be stopped first. + std::auto_ptr<ENUM_SERVICE_STATUS> deps; + DWORD numDeps = getDependentServices(svc, deps); + for (DWORD i = 0; i < numDeps; i++) + stop(deps.get()[i].lpServiceName); + + // Dependents stopped; send a stop code to the service. + SERVICE_STATUS_PROCESS ssp; + if (!::ControlService(svc, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp)) + throw qpid::Exception(QPID_MSG("Stopping " << serviceName << ": " << + qpid::sys::strError(::GetLastError()))); + + // Wait for the service to stop. + state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING); + if (state == SERVICE_STOPPED) + QPID_LOG(info, QPID_MSG("Service " << serviceName << + " stopped successfully.")); +} + +/** + * + */ +void SCM::openSvcManager() +{ + if (NULL != scmHandle) + return; + + scmHandle = ::OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // Rights + QPID_WINDOWS_CHECK_NULL(scmHandle); +} + +DWORD SCM::waitForStateChangeFrom(SC_HANDLE svc, DWORD originalState) +{ + SERVICE_STATUS_PROCESS ssStatus; + DWORD bytesNeeded; + DWORD waitTime; + if (!::QueryServiceStatusEx(svc, // handle to service + SC_STATUS_PROCESS_INFO, // information level + (LPBYTE)&ssStatus, // address of structure + sizeof(ssStatus), // size of structure + &bytesNeeded)) // size needed if buffer is too small + throw QPID_WINDOWS_ERROR(::GetLastError()); + + // Save the tick count and initial checkpoint. + DWORD startTickCount = ::GetTickCount(); + DWORD oldCheckPoint = ssStatus.dwCheckPoint; + + // Wait for the service to change out of the noted state. + while (ssStatus.dwCurrentState == originalState) { + // Do not wait longer than the wait hint. A good interval is + // one-tenth of the wait hint but not less than 1 second + // and not more than 10 seconds. + waitTime = ssStatus.dwWaitHint / 10; + if (waitTime < 1000) + waitTime = 1000; + else if (waitTime > 10000) + waitTime = 10000; + + ::Sleep(waitTime); + + // Check the status until the service is no longer stop pending. + if (!::QueryServiceStatusEx(svc, + SC_STATUS_PROCESS_INFO, + (LPBYTE) &ssStatus, + sizeof(ssStatus), + &bytesNeeded)) + throw QPID_WINDOWS_ERROR(::GetLastError()); + + if (ssStatus.dwCheckPoint > oldCheckPoint) { + // Continue to wait and check. + startTickCount = ::GetTickCount(); + oldCheckPoint = ssStatus.dwCheckPoint; + } else { + if ((::GetTickCount() - startTickCount) > ssStatus.dwWaitHint) + break; + } + } + return ssStatus.dwCurrentState; +} + +/** + * Get the services that depend on @arg svc. All dependent service info + * is returned in an array of ENUM_SERVICE_STATUS structures via @arg deps. + * + * @retval The number of dependent services. + */ +DWORD SCM::getDependentServices(SC_HANDLE svc, + std::auto_ptr<ENUM_SERVICE_STATUS>& deps) +{ + DWORD bytesNeeded; + DWORD numEntries; + + // Pass a zero-length buffer to get the required buffer size. + if (::EnumDependentServices(svc, + SERVICE_ACTIVE, + 0, + 0, + &bytesNeeded, + &numEntries)) { + // If the Enum call succeeds, then there are no dependent + // services, so do nothing. + return 0; + } + + if (::GetLastError() != ERROR_MORE_DATA) + throw QPID_WINDOWS_ERROR((::GetLastError())); + + // Allocate a buffer for the dependencies. + deps.reset((LPENUM_SERVICE_STATUS)(new char[bytesNeeded])); + // Enumerate the dependencies. + if (!::EnumDependentServices(svc, + SERVICE_ACTIVE, + deps.get(), + bytesNeeded, + &bytesNeeded, + &numEntries)) + throw QPID_WINDOWS_ERROR((::GetLastError())); + return numEntries; +} + +} } // namespace qpid::windows diff --git a/cpp/xml/cluster.xml b/cpp/xml/cluster.xml index 7b3f2fe63b..f9b8caf185 100644 --- a/cpp/xml/cluster.xml +++ b/cpp/xml/cluster.xml @@ -326,6 +326,12 @@ <field name="dequeueSincePurge" type="uint32"/> </control> + <!-- Replicate the internal state for an object - e.g. Links, bridges, etc --> + <control name="internal-state" code="0x42"> + <field name="type" type="str8"/> <!-- The type of object the state is for (e.g. 'link') --> + <field name="name" type="str8"/> <!-- Identifies the particular object to be updated --> + <field name="state" type="map"/> <!-- The internal state for the object --> + </control> </class> |