diff options
Diffstat (limited to 'cpp')
254 files changed, 5580 insertions, 17143 deletions
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 4c83540909..ada8f165ee 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -60,7 +60,6 @@ set (QPIDD_CONF_FILE ${QPID_INSTALL_CONFDIR}/qpidd.conf CACHE STRING "Name of the Qpid broker configuration file") install(FILES LICENSE NOTICE DESTINATION ${CMAKE_INSTALL_PREFIX}) -install(FILES xml/cluster.xml DESTINATION ${QPID_INSTALL_DATADIR}) if (WIN32) set (CMAKE_DEBUG_POSTFIX "d") diff --git a/cpp/configure.ac b/cpp/configure.ac index c68fed932e..c4e55f90de 100644 --- a/cpp/configure.ac +++ b/cpp/configure.ac @@ -256,54 +256,6 @@ AC_SUBST(DOWNLOAD_URL) AC_CHECK_HEADERS([boost/shared_ptr.hpp uuid/uuid.h],, AC_MSG_ERROR([Missing required header files.])) -# Check for optional cluster requirements. -tmp_LIBS=$LIBS -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], - [AS_HELP_STRING([--with-cpg], [Build with CPG support for clustering.])], - [case "${withval}" in - yes) # yes - require dependencies - test x$have_libcpg = xyes || AC_MSG_ERROR([libcpg not found, install openais-devel or corosync-devel]) - 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 ;; - *) AC_MSG_ERROR([Bad value ${withval} for --with-cpg option]) ;; - esac], - [ # not specified - use if present - test x$have_libcpg = xyes -a x$have_cpg_h = xyes && with_cpg=yes - ] -) -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], - [AS_HELP_STRING([--with-libcman], [Integration with libcman quorum service.])], - [case "${withval}" in - yes) # yes - require dependencies - test x$have_libcman = xyes || AC_MSG_ERROR([libcman not found, install cman-devel or cmanlib-devel]) - 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 ;; - *) AC_MSG_ERROR([Bad value ${withval} for --with-libcman option]) ;; - esac], - [ # not specified - use if present and we're using with_cpg - test x$have_libcman = xyes -a x$have_libcman_h = xyes -a x$with_cpg = xyes && with_libcman=yes - ] -) -AM_CONDITIONAL([HAVE_LIBCMAN], [test x$with_libcman = xyes]) - -LIBS=$tmp_LIBS - # Setup --with-sasl/--without-sasl as arguments to configure AC_ARG_WITH([sasl], [AS_HELP_STRING([--with-sasl], [Build with SASL authentication support])], diff --git a/cpp/include/qpid/client/SubscriptionManager.h b/cpp/include/qpid/client/SubscriptionManager.h index b69819a8ff..404a9c6eb9 100644 --- a/cpp/include/qpid/client/SubscriptionManager.h +++ b/cpp/include/qpid/client/SubscriptionManager.h @@ -280,8 +280,8 @@ class QPID_CLIENT_CLASS_EXTERN SubscriptionManager : public sys::Runnable, publi /** AutoCancel cancels a subscription in its destructor */ class AutoCancel { public: - AutoCancel(SubscriptionManager& sm_, const std::string& tag_) : sm(sm_), tag(tag_) {} - ~AutoCancel() { sm.cancel(tag); } + AutoCancel(SubscriptionManager&, const std::string& tag); + ~AutoCancel(); private: SubscriptionManager& sm; std::string tag; diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 526f191f84..ee9291a41b 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -167,7 +167,7 @@ inherit_value ("winver_LEGAL_COPYRIGHT" "") # check if we generate source as part of the build -# - rubygen generates the amqp spec and clustering +# - rubygen generates the amqp spec # - managementgen generates the broker management code # # rubygen subdir is excluded from stable distributions @@ -186,7 +186,7 @@ if (EXISTS ${AMQP_SPEC}) message(FATAL_ERROR "Can't locate python, needed to generate source files.") endif (NOT PYTHON_EXECUTABLE) - set(specs ${AMQP_SPEC} ${qpid-cpp_SOURCE_DIR}/xml/cluster.xml) + set(specs ${AMQP_SPEC}) set(regen_amqp OFF) set(rgen_dir ${qpid-cpp_SOURCE_DIR}/rubygen) file(GLOB_RECURSE rgen_progs ${rgen_dir}/*.rb) @@ -207,7 +207,6 @@ execute_process(COMMAND ${RUBY_EXECUTABLE} -I ${rgen_dir} ${rgen_dir}/generate $ set(mgmt_specs ${AMQP_SPEC_DIR}/management-schema.xml ${CMAKE_CURRENT_SOURCE_DIR}/qpid/acl/management-schema.xml - ${CMAKE_CURRENT_SOURCE_DIR}/qpid/cluster/management-schema.xml ${CMAKE_CURRENT_SOURCE_DIR}/qpid/ha/management-schema.xml ) set(mgen_dir ${qpid-cpp_SOURCE_DIR}/managementgen) @@ -672,9 +671,6 @@ if (BUILD_HA) COMPONENT ${QPID_COMPONENT_BROKER}) endif (BUILD_HA) -# Check for optional cluster support requirements -include (cluster.cmake) - # Check for optional RDMA support requirements include (rdma.cmake) @@ -1117,7 +1113,6 @@ set (qpidbroker_SOURCES qpid/broker/Exchange.cpp qpid/broker/ExpiryPolicy.cpp qpid/broker/Fairshare.cpp - qpid/broker/LegacyLVQ.cpp qpid/broker/MessageDeque.cpp qpid/broker/MessageMap.cpp qpid/broker/PriorityQueue.cpp @@ -1144,17 +1139,22 @@ set (qpidbroker_SOURCES qpid/broker/HeadersExchange.cpp qpid/broker/Link.cpp qpid/broker/LinkRegistry.cpp + qpid/broker/LossyQueue.cpp + qpid/broker/Lvq.cpp qpid/broker/Message.cpp qpid/broker/MessageAdapter.cpp qpid/broker/MessageBuilder.cpp + qpid/broker/MessageHandle.cpp qpid/broker/MessageStoreModule.cpp 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/QueueCursor.cpp + qpid/broker/QueueDepth.cpp + qpid/broker/QueueFactory.cpp qpid/broker/QueueRegistry.cpp + qpid/broker/QueueSettings.cpp qpid/broker/QueueFlowLimit.cpp qpid/broker/RecoveryManagerImpl.cpp qpid/broker/RecoveredEnqueue.cpp @@ -1177,8 +1177,8 @@ set (qpidbroker_SOURCES qpid/broker/TopicExchange.cpp qpid/broker/TxAccept.cpp qpid/broker/TxBuffer.cpp - qpid/broker/TxPublish.cpp qpid/broker/Vhost.cpp + qpid/broker/amqp_0_10/MessageTransfer.cpp qpid/management/ManagementAgent.cpp qpid/management/ManagementDirectExchange.cpp qpid/management/ManagementTopicExchange.cpp @@ -1433,45 +1433,6 @@ install (FILES ${qmfconsole_HEADERS} COMPONENT ${QPID_COMPONENT_QMF}) install_pdb (qmfconsole ${QPID_COMPONENT_QMF}) -# A queue event listener plugin that creates messages on a replication -# queue corresponding to enqueue and dequeue events: -set (replicating_listener_SOURCES - qpid/replication/constants.h - qpid/replication/ReplicatingEventListener.cpp - qpid/replication/ReplicatingEventListener.h - ) -add_msvc_version (replicating_listener library dll) -add_library (replicating_listener MODULE ${replicating_listener_SOURCES}) -target_link_libraries (replicating_listener qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY}) -set_target_properties (replicating_listener PROPERTIES PREFIX "") -if (CMAKE_COMPILER_IS_GNUCXX) - set_target_properties(replicating_listener PROPERTIES - LINK_FLAGS "${GCC_CATCH_UNDEFINED}") -endif (CMAKE_COMPILER_IS_GNUCXX) -install (TARGETS replicating_listener - DESTINATION ${QPIDD_MODULE_DIR} - COMPONENT ${QPID_COMPONENT_BROKER}) - -# A custom exchange plugin that allows an exchange to be created that -# can process the messages from a replication queue (populated on the -# source system by the replicating listener plugin above) and take the -# corresponding action on the local queues -set (replication_exchange_SOURCES - qpid/replication/constants.h - qpid/replication/ReplicationExchange.cpp - qpid/replication/ReplicationExchange.h - ) -add_msvc_version (replication_exchange library dll) -add_library (replication_exchange MODULE ${replication_exchange_SOURCES}) -target_link_libraries (replication_exchange qpidbroker) -set_target_properties (replication_exchange PROPERTIES PREFIX "") -if (CMAKE_COMPILER_IS_GNUCXX) - set_target_properties(replication_exchange PROPERTIES - LINK_FLAGS "${GCC_CATCH_UNDEFINED}") -endif (CMAKE_COMPILER_IS_GNUCXX) -install (TARGETS replication_exchange - DESTINATION ${QPIDD_MODULE_DIR} - COMPONENT ${QPID_COMPONENT_BROKER}) # This is only really needed until all the trunk builds (Linux, UNIX, Windows) # are all on cmake only. This is because cmake builds always have a config.h diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 8a8e4b1928..a65230b091 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -85,7 +85,7 @@ if GENERATE # AMQP_FINAL_XML is defined in ../configure.ac amqp_0_10_xml=@AMQP_FINAL_XML@ -specs=$(amqp_0_10_xml) $(top_srcdir)/xml/cluster.xml +specs=$(amqp_0_10_xml) # Ruby generator. rgen_dir=$(top_srcdir)/rubygen @@ -104,7 +104,6 @@ $(srcdir)/rubygen.cmake: $(rgen_generator) $(specs) mgen_dir=$(top_srcdir)/managementgen 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 -l -o qmf \ @@ -217,7 +216,6 @@ cmoduleexecdir=$(libdir)/qpid/client dmoduleexec_LTLIBRARIES = cmoduleexec_LTLIBRARIES = -include cluster.mk include ha.mk include acl.mk include qmf.mk @@ -225,7 +223,6 @@ include qmfc.mk if HAVE_XML include xml.mk endif -include replication.mk if RDMA @@ -292,7 +289,6 @@ endif EXTRA_DIST +=\ CMakeLists.txt \ - cluster.cmake \ config.h.cmake \ rdma.cmake \ ssl.cmake \ @@ -337,6 +333,7 @@ libqpidcommon_la_LIBADD = \ -lboost_program_options \ -lboost_filesystem \ -luuid \ + -lpthread \ $(LIB_DLOPEN) \ $(LIB_CLOCK_GETTIME) @@ -559,7 +556,6 @@ libqpidbroker_la_SOURCES = \ qpid/broker/Deliverable.h \ qpid/broker/DeliverableMessage.cpp \ qpid/broker/DeliverableMessage.h \ - qpid/broker/DeliveryAdapter.h \ qpid/broker/DeliveryId.h \ qpid/broker/DeliveryRecord.cpp \ qpid/broker/DeliveryRecord.h \ @@ -590,12 +586,14 @@ libqpidbroker_la_SOURCES = \ qpid/broker/HeadersExchange.cpp \ qpid/broker/HeadersExchange.h \ qpid/broker/AsyncCompletion.h \ - qpid/broker/LegacyLVQ.h \ - qpid/broker/LegacyLVQ.cpp \ + qpid/broker/IndexedDeque.h \ qpid/broker/Link.cpp \ qpid/broker/Link.h \ qpid/broker/LinkRegistry.cpp \ qpid/broker/LinkRegistry.h \ + qpid/broker/Lvq.h \ + qpid/broker/Lvq.cpp \ + qpid/broker/MapHandler.h \ qpid/broker/Message.cpp \ qpid/broker/Message.h \ qpid/broker/MessageAdapter.cpp \ @@ -630,19 +628,25 @@ libqpidbroker_la_SOURCES = \ qpid/broker/QueueBindings.h \ qpid/broker/QueueCleaner.cpp \ qpid/broker/QueueCleaner.h \ - qpid/broker/QueueEvents.cpp \ - qpid/broker/QueueEvents.h \ + qpid/broker/QueueCursor.h \ + qpid/broker/QueueCursor.cpp \ + qpid/broker/QueueDepth.h \ + qpid/broker/QueueDepth.cpp \ + qpid/broker/QueueFactory.h \ + qpid/broker/QueueFactory.cpp \ + qpid/broker/QueueSettings.h \ + qpid/broker/QueueSettings.cpp \ qpid/broker/QueueListeners.cpp \ qpid/broker/QueueListeners.h \ qpid/broker/QueueObserver.h \ - qpid/broker/QueuePolicy.cpp \ - 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/LossyQueue.h \ + qpid/broker/LossyQueue.cpp \ qpid/broker/RecoverableConfig.h \ qpid/broker/RecoverableExchange.h \ qpid/broker/RecoverableMessage.h \ @@ -693,9 +697,6 @@ libqpidbroker_la_SOURCES = \ qpid/broker/TxBuffer.cpp \ qpid/broker/TxBuffer.h \ qpid/broker/TxOp.h \ - qpid/broker/TxOpVisitor.h \ - qpid/broker/TxPublish.cpp \ - qpid/broker/TxPublish.h \ qpid/broker/Vhost.cpp \ qpid/broker/Vhost.h \ qpid/broker/MessageDistributor.h \ @@ -703,6 +704,8 @@ libqpidbroker_la_SOURCES = \ qpid/broker/FifoDistributor.cpp \ qpid/broker/MessageGroupManager.cpp \ qpid/broker/MessageGroupManager.h \ + qpid/broker/amqp_0_10/MessageTransfer.h \ + qpid/broker/amqp_0_10/MessageTransfer.cpp \ qpid/management/ManagementAgent.cpp \ qpid/management/ManagementAgent.h \ qpid/management/ManagementDirectExchange.cpp \ diff --git a/cpp/src/asyncstore.cmake b/cpp/src/asyncstore.cmake index 6171ed5505..e6230483ea 100644 --- a/cpp/src/asyncstore.cmake +++ b/cpp/src/asyncstore.cmake @@ -51,6 +51,7 @@ set (asyncStore_SOURCES qpid/asyncStore/EventHandleImpl.cpp qpid/asyncStore/MessageHandleImpl.cpp qpid/asyncStore/OperationQueue.cpp + qpid/asyncStore/PersistableMessageContext.cpp qpid/asyncStore/Plugin.cpp qpid/asyncStore/QueueHandleImpl.cpp qpid/asyncStore/RunState.cpp diff --git a/cpp/src/cluster.cmake b/cpp/src/cluster.cmake deleted file mode 100644 index 815cdffdec..0000000000 --- a/cpp/src/cluster.cmake +++ /dev/null @@ -1,175 +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. -# -# -# Cluster library CMake fragment, to be included in CMakeLists.txt -# - -# Optional cluster support. Requires CPG; if building it, can optionally -# include CMAN support as well. - -include(CheckIncludeFiles) -include(CheckLibraryExists) - -set(LIBCPG_PATH /usr/lib/openais /usr/lib64/openais /usr/lib/corosync /usr/lib64/corosync CACHE STRING "Default locations for libcpg (cluster library)" ) -find_library(LIBCPG cpg ${LIBCPG_PATH}) -if (LIBCPG) - CHECK_LIBRARY_EXISTS (${LIBCPG} cpg_local_get "" HAVE_LIBCPG) - CHECK_INCLUDE_FILES (openais/cpg.h HAVE_OPENAIS_CPG_H) - CHECK_INCLUDE_FILES (corosync/cpg.h HAVE_COROSYNC_CPG_H) -endif (LIBCPG) - -set (cluster_default ${cluster_force}) -if (CMAKE_SYSTEM_NAME STREQUAL Windows) -else (CMAKE_SYSTEM_NAME STREQUAL Windows) - if (HAVE_LIBCPG) - if (HAVE_OPENAIS_CPG_H OR HAVE_COROSYNC_CPG_H) - set (cluster_default ON) - endif (HAVE_OPENAIS_CPG_H OR HAVE_COROSYNC_CPG_H) - endif (HAVE_LIBCPG) -endif (CMAKE_SYSTEM_NAME STREQUAL Windows) - -option(BUILD_CLUSTER "Build with CPG support for clustering" ${cluster_default}) -if (BUILD_CLUSTER) - - if (NOT HAVE_LIBCPG) - message(FATAL_ERROR "libcpg not found, install openais-devel or corosync-devel") - endif (NOT HAVE_LIBCPG) - if (NOT HAVE_OPENAIS_CPG_H AND NOT HAVE_COROSYNC_CPG_H) - message(FATAL_ERROR "cpg.h not found, install openais-devel or corosync-devel") - endif (NOT HAVE_OPENAIS_CPG_H AND NOT HAVE_COROSYNC_CPG_H) - - CHECK_LIBRARY_EXISTS (cman cman_is_quorate "" HAVE_LIBCMAN) - CHECK_INCLUDE_FILES (libcman.h HAVE_LIBCMAN_H) - - set(cluster_quorum_default ${cluster_quorum_force}) - if (HAVE_LIBCMAN AND HAVE_LIBCMAN_H) - set(cluster_quorum_default ON) - endif (HAVE_LIBCMAN AND HAVE_LIBCMAN_H) - - option(BUILD_CLUSTER_QUORUM "Include libcman quorum service integration" ${cluster_quorum_default}) - if (BUILD_CLUSTER_QUORUM) - if (NOT HAVE_LIBCMAN) - message(FATAL_ERROR "libcman not found, install cman-devel or cmanlib-devel") - endif (NOT HAVE_LIBCMAN) - if (NOT HAVE_LIBCMAN_H) - message(FATAL_ERROR "libcman.h not found, install cman-devel or cmanlib-devel") - endif (NOT HAVE_LIBCMAN_H) - - set (CMAN_SOURCES qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp) - set (CMAN_LIB cman) - else (BUILD_CLUSTER_QUORUM) - set (CMAN_SOURCES qpid/cluster/Quorum_null.h) - endif (BUILD_CLUSTER_QUORUM) - - set (cluster_SOURCES - ${CMAN_SOURCES} - qpid/cluster/Cluster.cpp - qpid/cluster/Cluster.h - qpid/cluster/ClusterTimer.cpp - qpid/cluster/ClusterTimer.h - qpid/cluster/Decoder.cpp - qpid/cluster/Decoder.h - qpid/cluster/PollableQueue.h - qpid/cluster/ClusterMap.cpp - qpid/cluster/ClusterMap.h - qpid/cluster/ClusterPlugin.cpp - qpid/cluster/ClusterSettings.h - qpid/cluster/Connection.cpp - qpid/cluster/Connection.h - qpid/cluster/ConnectionCodec.cpp - qpid/cluster/ConnectionCodec.h - qpid/cluster/Cpg.cpp - qpid/cluster/Cpg.h - qpid/cluster/CredentialsExchange.cpp - qpid/cluster/CredentialsExchange.h - qpid/cluster/Dispatchable.h - qpid/cluster/UpdateClient.cpp - qpid/cluster/UpdateClient.h - qpid/cluster/RetractClient.cpp - qpid/cluster/RetractClient.h - qpid/cluster/ErrorCheck.cpp - qpid/cluster/ErrorCheck.h - qpid/cluster/Event.cpp - qpid/cluster/Event.h - qpid/cluster/EventFrame.h - qpid/cluster/EventFrame.cpp - qpid/cluster/ExpiryPolicy.h - qpid/cluster/ExpiryPolicy.cpp - qpid/cluster/FailoverExchange.cpp - qpid/cluster/FailoverExchange.h - qpid/cluster/UpdateExchange.cpp - qpid/cluster/UpdateExchange.h - qpid/cluster/UpdateReceiver.h - qpid/cluster/LockedConnectionMap.h - qpid/cluster/Multicaster.cpp - qpid/cluster/Multicaster.h - qpid/cluster/McastFrameHandler.h - qpid/cluster/NoOpConnectionOutputHandler.h - qpid/cluster/Numbering.h - qpid/cluster/OutputInterceptor.cpp - qpid/cluster/OutputInterceptor.h - qpid/cluster/PollerDispatch.cpp - qpid/cluster/PollerDispatch.h - qpid/cluster/ProxyInputHandler.h - qpid/cluster/Quorum.h - qpid/cluster/InitialStatusMap.h - qpid/cluster/InitialStatusMap.cpp - qpid/cluster/MemberSet.h - qpid/cluster/MemberSet.cpp - qpid/cluster/types.h - qpid/cluster/SecureConnectionFactory.h - qpid/cluster/SecureConnectionFactory.cpp - qpid/cluster/StoreStatus.h - qpid/cluster/StoreStatus.cpp - qpid/cluster/UpdateDataExchange.h - qpid/cluster/UpdateDataExchange.cpp - ) - - add_library (cluster MODULE ${cluster_SOURCES}) - target_link_libraries (cluster ${LIBCPG} ${CMAN_LIB} qpidbroker qpidclient ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}) - set_target_properties (cluster PROPERTIES PREFIX "") - - # Create a second shared library for linking with test executables, - # cmake will not allow a module to be linked with an executable. - add_library (cluster_shared SHARED ${cluster_SOURCES}) - target_link_libraries (cluster_shared ${LIBCPG} ${CMAN_LIB} qpidbroker qpidclient ${Boost_FILESYSTEM_LIBRARY}) - - if (CMAKE_COMPILER_IS_GNUCXX) - # Turn off optimisation based on strict-aliasing because we get warnings about violations - set_target_properties(cluster cluster_shared PROPERTIES - COMPILE_FLAGS "-fno-strict-aliasing" - LINK_FLAGS "${GCC_CATCH_UNDEFINED} -pthread") - endif (CMAKE_COMPILER_IS_GNUCXX) - - add_library (watchdog MODULE qpid/cluster/WatchDogPlugin.cpp) - set_target_properties (watchdog PROPERTIES PREFIX "") - - add_executable(qpidd_watchdog qpid/cluster/qpidd_watchdog.cpp) - - install (TARGETS cluster watchdog - DESTINATION ${QPIDD_MODULE_DIR} - COMPONENT ${QPID_COMPONENT_BROKER}) - install (TARGETS qpidd_watchdog - DESTINATION ${QPID_LIBEXEC_DIR} - COMPONENT ${QPID_COMPONENT_BROKER}) - -endif (BUILD_CLUSTER) - -# Distribute all sources. -#EXTRA_DIST += qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp qpid/cluster/Quorum_null.h diff --git a/cpp/src/cluster.mk b/cpp/src/cluster.mk deleted file mode 100644 index 632522e84f..0000000000 --- a/cpp/src/cluster.mk +++ /dev/null @@ -1,115 +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. -# -# -# Cluster library makefile fragment, to be included in Makefile.am -# - -# Optional CMAN support - -# Distribute all sources. -EXTRA_DIST += qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp qpid/cluster/Quorum_null.h - -if HAVE_LIBCMAN -CMAN_SOURCES = qpid/cluster/Quorum_cman.h qpid/cluster/Quorum_cman.cpp -libcman = -lcman -else -CMAN_SOURCES = qpid/cluster/Quorum_null.h -endif - -if HAVE_LIBCPG - -dmoduleexec_LTLIBRARIES += cluster.la - -cluster_la_SOURCES = \ - $(CMAN_SOURCES) \ - qpid/cluster/Cluster.cpp \ - qpid/cluster/Cluster.h \ - qpid/cluster/ClusterTimer.cpp \ - qpid/cluster/ClusterTimer.h \ - qpid/cluster/Decoder.cpp \ - qpid/cluster/Decoder.h \ - qpid/cluster/PollableQueue.h \ - qpid/cluster/ClusterMap.cpp \ - qpid/cluster/ClusterMap.h \ - qpid/cluster/ClusterPlugin.cpp \ - qpid/cluster/ClusterSettings.h \ - qpid/cluster/Connection.cpp \ - qpid/cluster/Connection.h \ - qpid/cluster/ConnectionCodec.cpp \ - qpid/cluster/ConnectionCodec.h \ - qpid/cluster/Cpg.cpp \ - qpid/cluster/Cpg.h \ - qpid/cluster/CredentialsExchange.cpp \ - qpid/cluster/CredentialsExchange.h \ - qpid/cluster/Dispatchable.h \ - qpid/cluster/UpdateClient.cpp \ - qpid/cluster/UpdateClient.h \ - qpid/cluster/RetractClient.cpp \ - qpid/cluster/RetractClient.h \ - qpid/cluster/ErrorCheck.cpp \ - qpid/cluster/ErrorCheck.h \ - qpid/cluster/Event.cpp \ - qpid/cluster/Event.h \ - qpid/cluster/EventFrame.h \ - qpid/cluster/EventFrame.cpp \ - qpid/cluster/ExpiryPolicy.h \ - qpid/cluster/ExpiryPolicy.cpp \ - qpid/cluster/FailoverExchange.cpp \ - qpid/cluster/FailoverExchange.h \ - qpid/cluster/UpdateExchange.h \ - qpid/cluster/UpdateExchange.cpp \ - qpid/cluster/UpdateReceiver.h \ - qpid/cluster/LockedConnectionMap.h \ - qpid/cluster/Multicaster.cpp \ - qpid/cluster/Multicaster.h \ - qpid/cluster/McastFrameHandler.h \ - qpid/cluster/NoOpConnectionOutputHandler.h \ - qpid/cluster/Numbering.h \ - qpid/cluster/OutputInterceptor.cpp \ - qpid/cluster/OutputInterceptor.h \ - qpid/cluster/PollerDispatch.cpp \ - qpid/cluster/PollerDispatch.h \ - qpid/cluster/ProxyInputHandler.h \ - qpid/cluster/Quorum.h \ - qpid/cluster/InitialStatusMap.h \ - qpid/cluster/InitialStatusMap.cpp \ - qpid/cluster/MemberSet.h \ - qpid/cluster/MemberSet.cpp \ - qpid/cluster/types.h \ - qpid/cluster/SecureConnectionFactory.h \ - qpid/cluster/SecureConnectionFactory.cpp \ - qpid/cluster/StoreStatus.h \ - qpid/cluster/StoreStatus.cpp \ - qpid/cluster/UpdateDataExchange.h \ - qpid/cluster/UpdateDataExchange.cpp - -cluster_la_LIBADD= -lcpg $(libcman) libqpidbroker.la libqpidclient.la -cluster_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing -cluster_la_LDFLAGS = $(PLUGINLDFLAGS) - -# The watchdog plugin and helper executable -dmoduleexec_LTLIBRARIES += watchdog.la -watchdog_la_SOURCES = qpid/cluster/WatchDogPlugin.cpp -watchdog_la_LIBADD = libqpidbroker.la -watchdog_la_LDFLAGS = $(PLUGINLDFLAGS) - -qpidexec_PROGRAMS += qpidd_watchdog -qpidd_watchdog_SOURCES = qpid/cluster/qpidd_watchdog.cpp - -endif # HAVE_LIBCPG diff --git a/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp index d941577f6a..89c4b3402a 100644 --- a/cpp/src/qpid/acl/Acl.cpp +++ b/cpp/src/qpid/acl/Acl.cpp @@ -129,6 +129,13 @@ bool Acl::approveConnection(const qpid::broker::Connection& conn) return connectionCounter->approveConnection(conn); } + +void Acl::setUserId(const qpid::broker::Connection& connection, const std::string& username) +{ + connectionCounter->setUserId(connection, username); +} + + bool Acl::result( const AclResult& aclreslt, const std::string& id, diff --git a/cpp/src/qpid/acl/Acl.h b/cpp/src/qpid/acl/Acl.h index 4893f71ef2..4787934275 100644 --- a/cpp/src/qpid/acl/Acl.h +++ b/cpp/src/qpid/acl/Acl.h @@ -94,6 +94,8 @@ public: virtual bool approveConnection(const broker::Connection& connection); + virtual void setUserId(const broker::Connection& connection, const std::string& username); + virtual ~Acl(); private: bool result( diff --git a/cpp/src/qpid/acl/AclConnectionCounter.cpp b/cpp/src/qpid/acl/AclConnectionCounter.cpp index 052fa3c222..8c6e3eef6e 100644 --- a/cpp/src/qpid/acl/AclConnectionCounter.cpp +++ b/cpp/src/qpid/acl/AclConnectionCounter.cpp @@ -296,6 +296,47 @@ bool ConnectionCounter::approveConnection(const broker::Connection& connection) } } + +// +// setUserId +// On cluster shadow connections, track a new user id for this connection. +// +void ConnectionCounter::setUserId(const broker::Connection& connection, + const std::string& username) +{ + Mutex::ScopedLock locker(dataLock); + + connectCountsMap_t::iterator eRef = connectProgressMap.find(connection.getMgmtId()); + if (eRef != connectProgressMap.end()) { + if ((*eRef).second == C_OPENED){ + // Connection has been opened so that current user has been counted + if (connection.isShadow()) { + // This is a shadow connection and therefore receives userId changes + QPID_LOG(debug, "Changing User ID for cluster connection: " + << connection.getMgmtId() << ", old user:'" << connection.getUserId() + << "', new user:'" << username << "'"); + + // Decrement user in-use count for old userId + releaseLH(connectByNameMap, + connection.getUserId(), + nameLimit); + // Increment user in-use count for new userId + (void) countConnectionLH(connectByNameMap, username, nameLimit, false); + } else { + QPID_LOG(warning, "Changing User ID for non-cluster connections is not supported: " + << connection.getMgmtId() << ", old user " << connection.getUserId() + << ", new user " << username); + } + } else { + // connection exists but has not been opened. + // setUserId is called in normal course. The user gets counted when connection is opened. + } + } else { + // Connection does not exist. + } +} + + // // getClientIp - given a connection's mgmtId return the client host part. // diff --git a/cpp/src/qpid/acl/AclConnectionCounter.h b/cpp/src/qpid/acl/AclConnectionCounter.h index eec8e90256..54fa6933ff 100644 --- a/cpp/src/qpid/acl/AclConnectionCounter.h +++ b/cpp/src/qpid/acl/AclConnectionCounter.h @@ -94,6 +94,7 @@ public: // Connection counting bool approveConnection(const broker::Connection& conn); + void setUserId(const broker::Connection& connection, const std::string& username); }; }} // namespace qpid::ha diff --git a/cpp/src/qpid/acl/AclData.cpp b/cpp/src/qpid/acl/AclData.cpp index a07176dc76..7c14d0985d 100644 --- a/cpp/src/qpid/acl/AclData.cpp +++ b/cpp/src/qpid/acl/AclData.cpp @@ -25,6 +25,13 @@ namespace qpid { namespace acl { // + // Instantiate the substitution keyword string + // + const std::string AclData::USER_SUBSTITUTION_KEYWORD = "${user}"; + const std::string AclData::DOMAIN_SUBSTITUTION_KEYWORD = "${domain}"; + const std::string AclData::USERDOMAIN_SUBSTITUTION_KEYWORD = "${userdomain}"; + + // // constructor // AclData::AclData(): @@ -147,7 +154,17 @@ namespace acl { // the calling args and not in the param map. if (rulePropMapItr->first == acl::SPECPROP_NAME) { - if (matchProp(rulePropMapItr->second, name)) + // substitute user name into object name + bool result; + if (rsItr->ruleHasUserSub[PROP_NAME]) { + std::string sName(rulePropMapItr->second); + substituteUserId(sName, id); + result = matchProp(sName, name); + } else { + result = matchProp(rulePropMapItr->second, name); + } + + if (result) { QPID_LOG(debug, "ACL: lookup name '" << name << "' matched with rule name '" @@ -222,7 +239,20 @@ namespace acl { break; default: - if (matchProp(rulePropMapItr->second, lookupParamItr->second)) + bool result; + if ((SPECPROP_ALTERNATE == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ALTERNATE]) || + (SPECPROP_ROUTINGKEY == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) || + (SPECPROP_QUEUENAME == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_QUEUENAME])) + { + // These properties are allowed to have username substitution + std::string sName(rulePropMapItr->second); + substituteUserId(sName, id); + result = matchProp(sName, lookupParamItr->second); + } else { + result = matchProp(rulePropMapItr->second, lookupParamItr->second); + } + + if (result) { QPID_LOG(debug, "ACL: the pair(" << AclHelper::getPropertyStr(lookupParamItr->first) @@ -346,7 +376,18 @@ namespace acl { bool match =true; if (rsItr->pubExchNameInRule) { - if (matchProp(rsItr->pubExchName, name)) + // substitute user name into object name + bool result; + + if (rsItr->ruleHasUserSub[PROP_NAME]) { + std::string sName(rsItr->pubExchName); + substituteUserId(sName, id); + result = matchProp(sName, name); + } else { + result = matchProp(rsItr->pubExchName, name); + } + + if (result) { QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '" << name << "' matched with rule name '" @@ -364,18 +405,40 @@ namespace acl { if (match && rsItr->pubRoutingKeyInRule) { - if (rsItr->matchRoutingKey(routingKey)) + if ((routingKey.find(USER_SUBSTITUTION_KEYWORD, 0) != std::string::npos) || + (routingKey.find(DOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos) || + (routingKey.find(USERDOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos)) { - QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '" - << routingKey << "' matched with rule routing key '" - << rsItr->pubRoutingKey << "'"); + // The user is not allowed to present a routing key with the substitution key in it + QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << + " User-specified routing key has substitution wildcard:" << routingKey + << ". Rule match prohibited."); + match = false; } else { - QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '" - << routingKey << "' did not match with rule routing key '" - << rsItr->pubRoutingKey << "'"); - match = false; + bool result; + if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) { + std::string sKey(routingKey); + substituteKeywords(sKey, id); + result = rsItr->matchRoutingKey(sKey); + } else { + result = rsItr->matchRoutingKey(routingKey); + } + + if (result) + { + QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '" + << routingKey << "' matched with rule routing key '" + << rsItr->pubRoutingKey << "'"); + } + else + { + QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '" + << routingKey << "' did not match with rule routing key '" + << rsItr->pubRoutingKey << "'"); + match = false; + } } } @@ -501,4 +564,102 @@ namespace acl { return true; } + const std::string DOMAIN_SEPARATOR("@"); + const std::string PERIOD("."); + const std::string UNDERSCORE("_"); + // + // substituteString + // Given a name string from an Acl rule, substitute the replacement into it + // wherever the placeholder directs. + // + void AclData::substituteString(std::string& targetString, + const std::string& placeholder, + const std::string& replacement) + { + assert (!placeholder.empty()); + if (placeholder.empty()) + return; + size_t start_pos(0); + while((start_pos = targetString.find(placeholder, start_pos)) != std::string::npos) + { + targetString.replace(start_pos, placeholder.length(), replacement); + start_pos += replacement.length(); + } + } + + + // + // normalizeUserId + // Given a name string return it in a form usable as topic keys: + // change "@" and "." to "_". + // + std::string AclData::normalizeUserId(const std::string& userId) + { + std::string normalId(userId); + substituteString(normalId, DOMAIN_SEPARATOR, UNDERSCORE); + substituteString(normalId, PERIOD, UNDERSCORE); + return normalId; + } + + + // + // substituteUserId + // Given an Acl rule and an authenticated userId + // do the keyword substitutions on the rule. + // + void AclData::AclData::substituteUserId(std::string& ruleString, + const std::string& userId) + { + size_t locDomSeparator(0); + std::string user(""); + std::string domain(""); + std::string userdomain = normalizeUserId(userId); + + locDomSeparator = userId.find(DOMAIN_SEPARATOR); + if (std::string::npos == locDomSeparator) { + // "@" not found. There's just a user name + user = normalizeUserId(userId); + } else { + // "@" found, split the names. Domain may be blank. + user = normalizeUserId(userId.substr(0,locDomSeparator)); + domain = normalizeUserId(userId.substr(locDomSeparator+1)); + } + + substituteString(ruleString, USER_SUBSTITUTION_KEYWORD, user); + substituteString(ruleString, DOMAIN_SUBSTITUTION_KEYWORD, domain); + substituteString(ruleString, USERDOMAIN_SUBSTITUTION_KEYWORD, userdomain); + } + + + // + // substituteKeywords + // Given an Acl rule and an authenticated userId + // do reverse keyword substitutions on the rule. + // That is, replace the normalized name in the rule string with + // the keyword that represents it. This stragegy is used for + // topic key lookups where the keyword string proper is in the + // topic key search tree. + // + void AclData::AclData::substituteKeywords(std::string& ruleString, + const std::string& userId) + { + size_t locDomSeparator(0); + std::string user(""); + std::string domain(""); + std::string userdomain = normalizeUserId(userId); + + locDomSeparator = userId.find(DOMAIN_SEPARATOR); + if (std::string::npos == locDomSeparator) { + // "@" not found. There's just a user name + user = normalizeUserId(userId); + } else { + // "@" found, split the names + user = normalizeUserId(userId.substr(0,locDomSeparator)); + domain = normalizeUserId(userId.substr(locDomSeparator+1)); + } + std::string oRule(ruleString); + substituteString(ruleString, userdomain, USERDOMAIN_SUBSTITUTION_KEYWORD); + substituteString(ruleString, user, USER_SUBSTITUTION_KEYWORD); + substituteString(ruleString, domain, DOMAIN_SUBSTITUTION_KEYWORD); + } }} diff --git a/cpp/src/qpid/acl/AclData.h b/cpp/src/qpid/acl/AclData.h index ca0a676a1c..b4b13c44b6 100644 --- a/cpp/src/qpid/acl/AclData.h +++ b/cpp/src/qpid/acl/AclData.h @@ -62,6 +62,7 @@ public: boost::shared_ptr<topicTester> pTTest; bool pubExchNameInRule; std::string pubExchName; + std::vector<bool> ruleHasUserSub; Rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) : rawRuleNum(ruleNum), @@ -71,7 +72,8 @@ public: pubRoutingKey(), pTTest(boost::shared_ptr<topicTester>(new topicTester())), pubExchNameInRule(false), - pubExchName() + pubExchName(), + ruleHasUserSub(PROPERTYSIZE, false) {} @@ -132,6 +134,17 @@ public: bool matchProp(const std::string & src, const std::string& src1); void clear (); + static const std::string USER_SUBSTITUTION_KEYWORD; + static const std::string DOMAIN_SUBSTITUTION_KEYWORD; + static const std::string USERDOMAIN_SUBSTITUTION_KEYWORD; + void substituteString(std::string& targetString, + const std::string& placeholder, + const std::string& replacement); + std::string normalizeUserId(const std::string& userId); + void substituteUserId(std::string& ruleString, + const std::string& userId); + void substituteKeywords(std::string& ruleString, + const std::string& userId); AclData(); virtual ~AclData(); diff --git a/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp index f9be49b88d..fae67d0325 100644 --- a/cpp/src/qpid/acl/AclReader.cpp +++ b/cpp/src/qpid/acl/AclReader.cpp @@ -103,6 +103,15 @@ namespace acl { } else { AclData::Rule rule(cnt, (*i)->res, (*i)->props); + // Record which properties have the user substitution string + for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) { + if ((pItr->second.find(AclData::USER_SUBSTITUTION_KEYWORD, 0) != std::string::npos) || + (pItr->second.find(AclData::DOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos) || + (pItr->second.find(AclData::USERDOMAIN_SUBSTITUTION_KEYWORD, 0) != std::string::npos)) { + rule.ruleHasUserSub[pItr->first] = true; + } + } + // Action -> Object -> map<user -> set<Rule> > std::ostringstream actionstr; for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); diff --git a/cpp/src/qpid/asyncStore/PersistableMessageContext.cpp b/cpp/src/qpid/asyncStore/PersistableMessageContext.cpp new file mode 100644 index 0000000000..fd449e1b9c --- /dev/null +++ b/cpp/src/qpid/asyncStore/PersistableMessageContext.cpp @@ -0,0 +1,39 @@ +#include "PersistableMessageContext.h" + +namespace qpid { +namespace asyncStore { + +PersistableMessageContext::PersistableMessageContext(qpid::broker::AsyncStore* store) : m_store(store) {} + +PersistableMessageContext::~PersistableMessageContext() {} + +void +PersistableMessageContext::encode(qpid::framing::Buffer& /*buffer*/) const {} + +uint32_t +PersistableMessageContext::encodedSize() const { + return 0; +} + +bool +PersistableMessageContext::isPersistent() const { + return false; +} + +void +PersistableMessageContext::decodeHeader(framing::Buffer& /*buffer*/) {} + +void +PersistableMessageContext::decodeContent(framing::Buffer& /*buffer*/) {} + +uint32_t +PersistableMessageContext::encodedHeaderSize() const { + return 0; +} + +boost::intrusive_ptr<qpid::broker::PersistableMessage> PersistableMessageContext::merge(const std::map<std::string, qpid::types::Variant>& /*annotations*/) const { + boost::intrusive_ptr<qpid::broker::PersistableMessage> pmc; + return pmc; +} + +}} // namespace qpid::asyncStore diff --git a/cpp/src/qpid/asyncStore/PersistableMessageContext.h b/cpp/src/qpid/asyncStore/PersistableMessageContext.h new file mode 100644 index 0000000000..ec9eb377c2 --- /dev/null +++ b/cpp/src/qpid/asyncStore/PersistableMessageContext.h @@ -0,0 +1,32 @@ +#ifndef qpid_asyncStore_PersistableMessageContext_h_ +#define qpid_asyncStore_PersistableMessageContext_h_ + +#include "qpid/broker/MessageHandle.h" +#include "qpid/broker/PersistableMessage.h" + +namespace qpid { +namespace asyncStore { + +class PersistableMessageContext: public qpid::broker::PersistableMessage { +private: + qpid::broker::MessageHandle m_msgHandle; + qpid::broker::AsyncStore* m_store; +public: + PersistableMessageContext(qpid::broker::AsyncStore* store); + virtual ~PersistableMessageContext(); + + // --- Interface Persistable --- + void encode(qpid::framing::Buffer& buffer) const; + uint32_t encodedSize() const; + + // --- Class PersistableMessage --- + bool isPersistent() const; + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer); + uint32_t encodedHeaderSize() const; + boost::intrusive_ptr<PersistableMessage> merge(const std::map<std::string, qpid::types::Variant>& annotations) const; +}; + +}} // namespace qpid::asyncStore + +#endif // qpid_asyncStore_PersistableMessageContext_h_ diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h index 7c180439cf..4caf8ed3ce 100644 --- a/cpp/src/qpid/broker/AclModule.h +++ b/cpp/src/qpid/broker/AclModule.h @@ -78,7 +78,9 @@ namespace acl { PROP_SCHEMACLASS, PROP_POLICYTYPE, PROP_MAXQUEUESIZE, - PROP_MAXQUEUECOUNT }; + PROP_MAXQUEUECOUNT, + PROPERTYSIZE // PROPERTYSIZE must be last in list + }; // Property used in ACL spec file // Note for properties common to file processing/rule storage and to @@ -145,6 +147,10 @@ namespace broker { */ virtual bool approveConnection (const Connection& connection)=0; + /** Change connection's counted userId + */ + virtual void setUserId(const Connection& connection, const std::string& username)=0; + virtual ~AclModule() {}; }; } // namespace broker diff --git a/cpp/src/qpid/broker/AsyncCompletion.h b/cpp/src/qpid/broker/AsyncCompletion.h index fef994438f..0cf2856584 100644 --- a/cpp/src/qpid/broker/AsyncCompletion.h +++ b/cpp/src/qpid/broker/AsyncCompletion.h @@ -22,6 +22,7 @@ * */ +#include "qpid/RefCounted.h" #include <boost/intrusive_ptr.hpp> #include "qpid/broker/BrokerImportExport.h" @@ -77,7 +78,7 @@ namespace broker { * assuming no need for synchronization with Completer threads. */ -class AsyncCompletion +class AsyncCompletion : public virtual RefCounted { public: diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp index b763dd4119..2411e0520c 100644 --- a/cpp/src/qpid/broker/Broker.cpp +++ b/cpp/src/qpid/broker/Broker.cpp @@ -33,6 +33,7 @@ #include "qpid/broker/Link.h" #include "qpid/broker/ExpiryPolicy.h" #include "qpid/broker/QueueFlowLimit.h" +#include "qpid/broker/QueueSettings.h" #include "qpid/broker/MessageGroupManager.h" #include "qmf/org/apache/qpid/broker/Package.h" @@ -120,7 +121,6 @@ Broker::Options::Options(const std::string& name) : queueLimit(100*1048576/*100M default limit*/), tcpNoDelay(false), requireEncrypted(false), - asyncQueueEvents(false), // Must be false in a cluster. qmf2Support(true), qmf1Support(true), queueFlowStopRatio(80), @@ -164,7 +164,6 @@ Broker::Options::Options(const std::string& name) : ("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted") ("known-hosts-url", optValue(knownHosts, "URL or 'none'"), "URL to send as 'known-hosts' to clients ('none' implies empty list)") ("sasl-config", optValue(saslConfigPath, "DIR"), "gets sasl config info from nonstandard location") - ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication") ("default-flow-stop-threshold", optValue(queueFlowStopRatio, "PERCENT"), "Percent of queue's maximum capacity at which flow control is activated.") ("default-flow-resume-threshold", optValue(queueFlowResumeRatio, "PERCENT"), "Percent of queue's maximum capacity at which flow control is de-activated.") ("default-event-threshold-ratio", optValue(queueThresholdEventRatio, "%age of limit"), "The ratio of any specified queue limit at which an event will be raised") @@ -206,7 +205,6 @@ Broker::Broker(const Broker::Options& conf) : *this), mgmtObject(0), queueCleaner(queues, &timer), - queueEvents(poller,!conf.asyncQueueEvents), recovery(true), inCluster(false), clusterUpdatee(false), @@ -265,8 +263,6 @@ Broker::Broker(const Broker::Options& conf) : federationTag = conf.fedTag; } - QueuePolicy::setDefaultMaxSize(conf.queueLimit); - // Early-Initialize plugins Plugin::earlyInitAll(*this); @@ -430,7 +426,6 @@ void Broker::shutdown() { Broker::~Broker() { shutdown(); - queueEvents.shutdown(); finalize(); // Finalize any plugins. if (config.auth) SaslAuthenticator::fini(); @@ -694,11 +689,15 @@ void Broker::createObject(const std::string& type, const std::string& name, //treat everything else as extension properties else extensions[i->first] = i->second; } - framing::FieldTable arguments; - amqp_0_10::translate(extensions, arguments); + QueueSettings settings(durable, autodelete); + Variant::Map unused; + settings.populate(extensions, unused); + qpid::amqp_0_10::translate(unused, settings.storeSettings); + //TODO: unused doesn't take store settings into account... so can't yet implement strict + QPID_LOG(debug, "Broker did not use the following settings (store module may): " << unused); std::pair<boost::shared_ptr<Queue>, bool> result = - createQueue(name, durable, autodelete, 0, alternateExchange, arguments, userId, connectionId); + createQueue(name, settings, 0, alternateExchange, userId, connectionId); if (!result.second) { throw ObjectAlreadyExists(name); } @@ -1046,8 +1045,7 @@ Broker::getKnownBrokersImpl() return knownBrokers; } -bool Broker::deferDeliveryImpl(const std::string& , - const boost::intrusive_ptr<Message>& ) +bool Broker::deferDeliveryImpl(const std::string&, const Message&) { return false; } void Broker::setClusterTimer(std::auto_ptr<sys::Timer> t) { @@ -1061,23 +1059,21 @@ const std::string Broker::TCP_TRANSPORT("tcp"); std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( const std::string& name, - bool durable, - bool autodelete, + const QueueSettings& settings, const OwnershipToken* owner, const std::string& alternateExchange, - const qpid::framing::FieldTable& arguments, const std::string& userId, const std::string& connectionId) { if (acl) { std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); + params.insert(make_pair(acl::PROP_DURABLE, settings.durable ? _TRUE : _FALSE)); params.insert(make_pair(acl::PROP_EXCLUSIVE, owner ? _TRUE : _FALSE)); - params.insert(make_pair(acl::PROP_AUTODELETE, autodelete ? _TRUE : _FALSE)); - params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type"))); - params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count")))); - params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size")))); + params.insert(make_pair(acl::PROP_AUTODELETE, settings.autodelete ? _TRUE : _FALSE)); + params.insert(make_pair(acl::PROP_POLICYTYPE, settings.dropMessagesAtLimit ? "ring" : "reject")); + params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(settings.maxDepth.getCount()))); + params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(settings.maxDepth.getSize()))); if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId)); @@ -1089,7 +1085,7 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange)); } - std::pair<Queue::shared_ptr, bool> result = queues.declare(name, durable, autodelete, owner, alternate, arguments); + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, settings, alternate); if (result.second) { //add default binding: result.first->bind(exchanges.getDefault(), name); @@ -1100,16 +1096,16 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( //event instead? managementAgent->raiseEvent( _qmf::EventQueueDeclare(connectionId, userId, name, - durable, owner, autodelete, alternateExchange, - ManagementAgent::toMap(arguments), + settings.durable, owner, settings.autodelete, alternateExchange, + settings.asMap(), "created")); } QPID_LOG_CAT(debug, model, "Create queue. name:" << name << " user:" << userId << " rhost:" << connectionId - << " durable:" << (durable ? "T" : "F") + << " durable:" << (settings.durable ? "T" : "F") << " owner:" << owner - << " autodelete:" << (autodelete ? "T" : "F") + << " autodelete:" << (settings.autodelete ? "T" : "F") << " alternateExchange:" << alternateExchange ); } return result; diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h index 922d0558e5..d1be0f58da 100644 --- a/cpp/src/qpid/broker/Broker.h +++ b/cpp/src/qpid/broker/Broker.h @@ -34,7 +34,6 @@ #include "qpid/broker/LinkRegistry.h" #include "qpid/broker/SessionManager.h" #include "qpid/broker/QueueCleaner.h" -#include "qpid/broker/QueueEvents.h" #include "qpid/broker/Vhost.h" #include "qpid/broker/System.h" #include "qpid/broker/ExpiryPolicy.h" @@ -76,7 +75,7 @@ namespace broker { class ConnectionState; class ExpiryPolicy; class Message; - +struct QueueSettings; static const uint16_t DEFAULT_PORT=5672; struct NoSuchTransportException : qpid::Exception @@ -118,7 +117,6 @@ class Broker : public sys::Runnable, public Plugin::Target, bool requireEncrypted; std::string knownHosts; std::string saslConfigPath; - bool asyncQueueEvents; bool qmf2Support; bool qmf1Support; uint queueFlowStopRatio; // producer flow control: on @@ -178,11 +176,10 @@ class Broker : public sys::Runnable, public Plugin::Target, Vhost::shared_ptr vhostObject; System::shared_ptr systemObject; QueueCleaner queueCleaner; - QueueEvents queueEvents; std::vector<Url> knownBrokers; std::vector<Url> getKnownBrokersImpl(); bool deferDeliveryImpl(const std::string& queue, - const boost::intrusive_ptr<Message>& msg); + const Message& msg); std::string federationTag; bool recovery; bool inCluster, clusterUpdatee; @@ -227,7 +224,6 @@ class Broker : public sys::Runnable, public Plugin::Target, DtxManager& getDtxManager() { return dtxManager; } DataDir& getDataDir() { return dataDir; } Options& getOptions() { return config; } - QueueEvents& getQueueEvents() { return queueEvents; } void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { expiryPolicy = e; } boost::intrusive_ptr<ExpiryPolicy> getExpiryPolicy() { return expiryPolicy; } @@ -309,7 +305,8 @@ class Broker : public sys::Runnable, public Plugin::Target, * 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 Message& msg)> deferDelivery; bool isAuthenticating ( ) { return config.auth; } bool isTimestamping() { return config.timestampRcvMsgs; } @@ -318,11 +315,9 @@ class Broker : public sys::Runnable, public Plugin::Target, QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Queue>, bool> createQueue( const std::string& name, - bool durable, - bool autodelete, + const QueueSettings& settings, const OwnershipToken* owner, const std::string& alternateExchange, - const qpid::framing::FieldTable& arguments, const std::string& userId, const std::string& connectionId); diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp index 8d250a32e5..e68c906cc2 100644 --- a/cpp/src/qpid/broker/Connection.cpp +++ b/cpp/src/qpid/broker/Connection.cpp @@ -25,6 +25,7 @@ #include "qpid/broker/Bridge.h" #include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" +#include "qpid/broker/AclModule.h" #include "qpid/sys/SecuritySettings.h" #include "qpid/sys/ClusterSafe.h" @@ -278,6 +279,13 @@ void Connection::notifyConnectionForced(const string& text) void Connection::setUserId(const string& userId) { + // Account for changing userId + AclModule* acl = broker.getAcl(); + if (acl) + { + acl->setUserId(*this, userId); + } + ConnectionState::setUserId(userId); // In a cluster, the cluster code will raise the connect event // when the connection is replicated to the cluster. diff --git a/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h index 64073621be..64fc4288af 100644 --- a/cpp/src/qpid/broker/Consumer.h +++ b/cpp/src/qpid/broker/Consumer.h @@ -21,21 +21,23 @@ #ifndef _Consumer_ #define _Consumer_ -#include "qpid/broker/Message.h" -#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/QueueCursor.h" #include "qpid/broker/OwnershipToken.h" +#include <boost/shared_ptr.hpp> +#include <string> namespace qpid { namespace broker { +class DeliveryRecord; +class Message; class Queue; class QueueListeners; /** * Base class for consumers which represent a subscription to a queue. */ -class Consumer -{ +class Consumer : public QueueCursor { const bool acquires; // inListeners allows QueueListeners to efficiently track if this // instance is registered for notifications without having to @@ -47,22 +49,17 @@ class Consumer public: typedef boost::shared_ptr<Consumer> shared_ptr; - Consumer(const std::string& _name, bool preAcquires = true) - : acquires(preAcquires), inListeners(false), name(_name), position(0) {} + Consumer(const std::string& _name, SubscriptionType type) + : QueueCursor(type), acquires(type == CONSUMER), inListeners(false), name(_name) {} virtual ~Consumer(){} bool preAcquires() const { return acquires; } const std::string& getName() const { return name; } - /**@return the position of the last message seen by this consumer */ - virtual framing::SequenceNumber getPosition() const { return position; } - - virtual void setPosition(framing::SequenceNumber pos) { position = pos; } - - virtual bool deliver(QueuedMessage& msg) = 0; + virtual bool deliver(const QueueCursor& cursor, const Message& msg) = 0; virtual void notify() = 0; - virtual bool filter(boost::intrusive_ptr<Message>) { return true; } - virtual bool accept(boost::intrusive_ptr<Message>) { return true; } + virtual bool filter(const Message&) { return true; } + virtual bool accept(const Message&) { return true; } virtual OwnershipToken* getSession() = 0; virtual void cancel() = 0; @@ -75,7 +72,7 @@ class Consumer * Not to be confused with accept() above, which is asking if * this consumer will consume/browse the message. */ - virtual void acknowledged(const QueuedMessage&) = 0; + virtual void acknowledged(const DeliveryRecord&) = 0; /** Called if queue has been deleted, if true suppress the error message. * Used by HA ReplicatingSubscriptions where such errors are normal. @@ -83,7 +80,7 @@ class Consumer virtual bool hideDeletedError() { return false; } protected: - framing::SequenceNumber position; + //framing::SequenceNumber position; private: friend class QueueListeners; diff --git a/cpp/src/qpid/broker/Deliverable.h b/cpp/src/qpid/broker/Deliverable.h index ffb5a77bca..e08d0e1b20 100644 --- a/cpp/src/qpid/broker/Deliverable.h +++ b/cpp/src/qpid/broker/Deliverable.h @@ -21,17 +21,22 @@ #ifndef _Deliverable_ #define _Deliverable_ -#include "qpid/broker/Message.h" +#include "qpid/broker/AsyncCompletion.h" +#include "qpid/sys/IntegerTypes.h" +#include <boost/shared_ptr.hpp> namespace qpid { namespace broker { - class Deliverable{ + class Message; + class Queue; + + class Deliverable : public AsyncCompletion { public: bool delivered; Deliverable() : delivered(false) {} virtual Message& getMessage() = 0; - + virtual void deliverTo(const boost::shared_ptr<Queue>& queue) = 0; virtual uint64_t contentSize() { return 0; } virtual ~Deliverable(){} diff --git a/cpp/src/qpid/broker/DeliverableMessage.cpp b/cpp/src/qpid/broker/DeliverableMessage.cpp index 3ebb12461c..be4b7f0796 100644 --- a/cpp/src/qpid/broker/DeliverableMessage.cpp +++ b/cpp/src/qpid/broker/DeliverableMessage.cpp @@ -24,22 +24,20 @@ using namespace qpid::broker; -DeliverableMessage::DeliverableMessage(const boost::intrusive_ptr<Message>& _msg) : msg(_msg) -{ -} +DeliverableMessage::DeliverableMessage(const Message& _msg, TxBuffer* _txn) : msg(_msg), txn(_txn) {} void DeliverableMessage::deliverTo(const boost::shared_ptr<Queue>& queue) { - queue->deliver(msg); + queue->deliver(msg, txn); delivered = true; } Message& DeliverableMessage::getMessage() { - return *msg; + return msg; } -uint64_t DeliverableMessage::contentSize () +uint64_t DeliverableMessage::contentSize() { - return msg->contentSize (); + return msg.getContentSize(); } diff --git a/cpp/src/qpid/broker/DeliverableMessage.h b/cpp/src/qpid/broker/DeliverableMessage.h index c8d21001eb..d6d6bf5265 100644 --- a/cpp/src/qpid/broker/DeliverableMessage.h +++ b/cpp/src/qpid/broker/DeliverableMessage.h @@ -25,14 +25,15 @@ #include "qpid/broker/Deliverable.h" #include "qpid/broker/Message.h" -#include <boost/intrusive_ptr.hpp> - namespace qpid { namespace broker { - class QPID_BROKER_CLASS_EXTERN DeliverableMessage : public Deliverable{ - boost::intrusive_ptr<Message> msg; + class TxBuffer; + class QPID_BROKER_CLASS_EXTERN DeliverableMessage : public Deliverable + { + Message msg; + TxBuffer* txn; public: - QPID_BROKER_EXTERN DeliverableMessage(const boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN DeliverableMessage(const Message& msg, TxBuffer* txn); QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue); QPID_BROKER_EXTERN Message& getMessage(); QPID_BROKER_EXTERN uint64_t contentSize(); diff --git a/cpp/src/qpid/broker/DeliveryAdapter.h b/cpp/src/qpid/broker/DeliveryAdapter.h index b0bec60890..e69de29bb2 100644 --- a/cpp/src/qpid/broker/DeliveryAdapter.h +++ b/cpp/src/qpid/broker/DeliveryAdapter.h @@ -1,53 +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 _DeliveryAdapter_ -#define _DeliveryAdapter_ - -#include "qpid/broker/DeliveryId.h" -#include "qpid/broker/Message.h" -#include "qpid/framing/amqp_types.h" - -namespace qpid { -namespace broker { - -class DeliveryRecord; - -/** - * The intention behind this interface is to separate the generic - * handling of some form of message delivery to clients that is - * contained in the version independent Channel class from the - * details required for a particular situation or - * version. i.e. where the existing adapters allow (through - * supporting the generated interface for a version of the - * protocol) inputs of a channel to be adapted to the version - * independent part, this does the same for the outputs. - */ -class DeliveryAdapter -{ - public: - virtual void deliver(DeliveryRecord&, bool sync) = 0; - virtual ~DeliveryAdapter(){} -}; - -}} - - -#endif diff --git a/cpp/src/qpid/broker/DeliveryRecord.cpp b/cpp/src/qpid/broker/DeliveryRecord.cpp index 5d6aee9045..f547ee54c9 100644 --- a/cpp/src/qpid/broker/DeliveryRecord.cpp +++ b/cpp/src/qpid/broker/DeliveryRecord.cpp @@ -24,6 +24,7 @@ #include "qpid/broker/Consumer.h" #include "qpid/broker/Exchange.h" #include "qpid/broker/Queue.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/log/Statement.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/MessageTransferBody.h" @@ -32,77 +33,46 @@ using namespace qpid; using namespace qpid::broker; using std::string; -DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg, +DeliveryRecord::DeliveryRecord(const QueueCursor& _msg, + framing::SequenceNumber _msgId, const Queue::shared_ptr& _queue, const std::string& _tag, const boost::shared_ptr<Consumer>& _consumer, bool _acquired, bool accepted, bool _windowing, - uint32_t _credit): - msg(_msg), - queue(_queue), - tag(_tag), - consumer(_consumer), - acquired(_acquired), - acceptExpected(!accepted), - cancelled(false), - completed(false), - ended(accepted && acquired), - windowing(_windowing), - credit(msg.payload ? msg.payload->getRequiredCredit() : _credit) + uint32_t _credit) : msg(_msg), + queue(_queue), + tag(_tag), + consumer(_consumer), + acquired(_acquired), + acceptExpected(!accepted), + cancelled(false), + completed(false), + ended(accepted && acquired), + windowing(_windowing), + credit(_credit), + msgId(_msgId) {} bool DeliveryRecord::setEnded() { ended = true; - //reset msg pointer, don't need to hold on to it anymore - msg.payload = boost::intrusive_ptr<Message>(); QPID_LOG(debug, "DeliveryRecord::setEnded() id=" << id); return isRedundant(); } -void DeliveryRecord::redeliver(SemanticState* const session) { - if (!ended) { - if(cancelled){ - //if subscription was cancelled, requeue it (waiting for - //final confirmation for AMQP WG on this case) - requeue(); - }else{ - msg.payload->redeliver();//mark as redelivered - session->deliver(*this, false); - } - } -} - -void DeliveryRecord::deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize) -{ - id = deliveryId; - if (msg.payload->getRedelivered()){ - msg.payload->setRedelivered(); - } - msg.payload->adjustTtl(); - - framing::AMQFrame method((framing::MessageTransferBody(framing::ProtocolVersion(), tag, acceptExpected ? 0 : 1, acquired ? 0 : 1))); - method.setEof(false); - h.handle(method); - msg.payload->sendHeader(h, framesize); - msg.payload->sendContent(*queue, h, framesize); -} - -void DeliveryRecord::requeue() const +void DeliveryRecord::requeue() { if (acquired && !ended) { - msg.payload->redeliver(); - queue->requeue(msg); + queue->release(msg); } } void DeliveryRecord::release(bool setRedelivered) { if (acquired && !ended) { - if (setRedelivered) msg.payload->redeliver(); - queue->requeue(msg); + queue->release(msg, setRedelivered); acquired = false; setEnded(); } else { @@ -110,13 +80,14 @@ void DeliveryRecord::release(bool setRedelivered) } } -void DeliveryRecord::complete() { +void DeliveryRecord::complete() +{ completed = true; } bool DeliveryRecord::accept(TransactionContext* ctxt) { if (!ended) { - if (consumer) consumer->acknowledged(getMessage()); + if (consumer) consumer->acknowledged(*this); if (acquired) queue->dequeue(ctxt, msg); setEnded(); QPID_LOG(debug, "Accepted " << id); @@ -124,31 +95,22 @@ bool DeliveryRecord::accept(TransactionContext* ctxt) { return isRedundant(); } -void DeliveryRecord::dequeue(TransactionContext* ctxt) const{ +void DeliveryRecord::dequeue(TransactionContext* ctxt) const +{ if (acquired && !ended) { queue->dequeue(ctxt, msg); } } -void DeliveryRecord::committed() const{ +void DeliveryRecord::committed() const +{ queue->dequeueCommitted(msg); } void DeliveryRecord::reject() { if (acquired && !ended) { - Exchange::shared_ptr alternate = queue->getAlternateExchange(); - if (alternate) { - DeliverableMessage delivery(msg.payload); - alternate->routeWithAlternate(delivery); - QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to " - << alternate->getName()); - } else { - //just drop it - QPID_LOG(info, "Dropping rejected message from " << queue->getName()); - } - queue->countRejected(); - dequeue(); + queue->reject(msg); setEnded(); } } diff --git a/cpp/src/qpid/broker/DeliveryRecord.h b/cpp/src/qpid/broker/DeliveryRecord.h index 21074d4274..10436f3fa0 100644 --- a/cpp/src/qpid/broker/DeliveryRecord.h +++ b/cpp/src/qpid/broker/DeliveryRecord.h @@ -26,15 +26,17 @@ #include <deque> #include <vector> #include <ostream> +#include "qpid/framing/FrameHandler.h" #include "qpid/framing/SequenceSet.h" #include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/QueueCursor.h" #include "qpid/broker/DeliveryId.h" #include "qpid/broker/Message.h" namespace qpid { namespace broker { +class Queue; class TransactionContext; class SemanticState; struct AckRange; @@ -45,7 +47,7 @@ class Consumer; */ class DeliveryRecord { - QueuedMessage msg; + QueueCursor msg; mutable boost::shared_ptr<Queue> queue; std::string tag; // name of consumer boost::shared_ptr<Consumer> consumer; @@ -65,9 +67,10 @@ class DeliveryRecord * after that). */ uint32_t credit; + framing::SequenceNumber msgId; public: - QPID_BROKER_EXTERN DeliveryRecord(const QueuedMessage& msg, + QPID_BROKER_EXTERN DeliveryRecord(const QueueCursor& msgCursor, framing::SequenceNumber msgId, const boost::shared_ptr<Queue>& queue, const std::string& tag, const boost::shared_ptr<Consumer>& consumer, @@ -80,11 +83,10 @@ class DeliveryRecord bool coveredBy(const framing::SequenceSet* const range) const { return range->contains(id); } void dequeue(TransactionContext* ctxt = 0) const; - void requeue() const; + void requeue(); void release(bool setRedelivered); void reject(); void cancel(const std::string& tag); - void redeliver(SemanticState* const); void acquire(DeliveryIds& results); void complete(); bool accept(TransactionContext* ctxt); // Returns isRedundant() @@ -102,13 +104,13 @@ class DeliveryRecord uint32_t getCredit() const; const std::string& getTag() const { return tag; } - void deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize); void setId(DeliveryId _id) { id = _id; } typedef std::deque<DeliveryRecord> DeliveryRecords; static AckRange findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last); - const QueuedMessage& getMessage() const { return msg; } + const QueueCursor& getMessage() const { return msg; } framing::SequenceNumber getId() const { return id; } + framing::SequenceNumber getMessageId() const { return msgId; } boost::shared_ptr<Queue> getQueue() const { return queue; } friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&); diff --git a/cpp/src/qpid/broker/DtxAck.h b/cpp/src/qpid/broker/DtxAck.h index 16c3ff8ba0..10d63f5b0c 100644 --- a/cpp/src/qpid/broker/DtxAck.h +++ b/cpp/src/qpid/broker/DtxAck.h @@ -40,7 +40,6 @@ class DtxAck : public TxOp{ virtual void commit() throw(); virtual void rollback() throw(); virtual ~DtxAck(){} - virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } const DeliveryRecords& getPending() const { return pending; } }; diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp index 82d4b4df15..bb5dc2b807 100644 --- a/cpp/src/qpid/broker/Exchange.cpp +++ b/cpp/src/qpid/broker/Exchange.cpp @@ -25,6 +25,7 @@ #include "qpid/broker/ExchangeRegistry.h" #include "qpid/broker/FedOps.h" #include "qpid/broker/Queue.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/MessageProperties.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" @@ -62,10 +63,10 @@ Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) { if (parent->sequence){ parent->sequenceNo++; - msg.getMessage().insertCustomProperty(qpidMsgSequence,parent->sequenceNo); + msg.getMessage().addAnnotation(qpidMsgSequence,parent->sequenceNo); } if (parent->ive) { - parent->lastMsg = &( msg.getMessage()); + parent->lastMsg = msg.getMessage(); } } } @@ -111,12 +112,6 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) int count = 0; if (b.get()) { - // Block the content release if the message is transient AND there is more than one binding - if (!msg.getMessage().isPersistent() && b->size() > 1) { - msg.getMessage().blockContentRelease(); - } - - ExInfo error(getName()); // Save exception to throw at the end. for(std::vector<Binding::shared_ptr>::const_iterator i = b->begin(); i != b->end(); i++, count++) { try { @@ -161,8 +156,8 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) } void Exchange::routeIVE(){ - if (ive && lastMsg.get()){ - DeliverableMessage dmsg(lastMsg); + if (ive && lastMsg){ + DeliverableMessage dmsg(lastMsg, 0); route(dmsg); } } @@ -400,9 +395,9 @@ bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b) return b->queue == queue; } -void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) { - msg->setExchange(getName()); -} +//void Exchange::setProperties(Message& msg) { +// qpid::broker::amqp_0_10::MessageTransfer::setExchange(msg, getName()); +//} bool Exchange::routeWithAlternate(Deliverable& msg) { diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h index fba752210f..2b2f7db934 100644 --- a/cpp/src/qpid/broker/Exchange.h +++ b/cpp/src/qpid/broker/Exchange.h @@ -25,6 +25,7 @@ #include <boost/shared_ptr.hpp> #include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/Deliverable.h" +#include "qpid/broker/Message.h" #include "qpid/broker/MessageStore.h" #include "qpid/broker/PersistableExchange.h" #include "qpid/framing/FieldTable.h" @@ -74,7 +75,7 @@ protected: mutable qpid::sys::Mutex sequenceLock; int64_t sequenceNo; bool ive; - boost::intrusive_ptr<Message> lastMsg; + Message lastMsg; class PreRoute{ public: @@ -196,7 +197,7 @@ public: virtual bool bind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; 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>&); + //QPID_BROKER_EXTERN virtual void setProperties(Message&); virtual void route(Deliverable& msg) = 0; //PersistableExchange: diff --git a/cpp/src/qpid/broker/ExpiryPolicy.cpp b/cpp/src/qpid/broker/ExpiryPolicy.cpp index 62cb3fc116..687eac7817 100644 --- a/cpp/src/qpid/broker/ExpiryPolicy.cpp +++ b/cpp/src/qpid/broker/ExpiryPolicy.cpp @@ -27,7 +27,7 @@ namespace broker { ExpiryPolicy::~ExpiryPolicy() {} -bool ExpiryPolicy::hasExpired(Message& m) { +bool ExpiryPolicy::hasExpired(const Message& m) { return m.getExpiration() < sys::AbsTime::now(); } diff --git a/cpp/src/qpid/broker/ExpiryPolicy.h b/cpp/src/qpid/broker/ExpiryPolicy.h index 2caf00ce00..1fb41ccd29 100644 --- a/cpp/src/qpid/broker/ExpiryPolicy.h +++ b/cpp/src/qpid/broker/ExpiryPolicy.h @@ -42,7 +42,7 @@ class QPID_BROKER_CLASS_EXTERN ExpiryPolicy : public RefCounted { public: QPID_BROKER_EXTERN virtual ~ExpiryPolicy(); - QPID_BROKER_EXTERN virtual bool hasExpired(Message&); + QPID_BROKER_EXTERN virtual bool hasExpired(const Message&); QPID_BROKER_EXTERN virtual qpid::sys::AbsTime getCurrentTime(); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Fairshare.cpp b/cpp/src/qpid/broker/Fairshare.cpp index 7cdad1a44f..ec8ae9a037 100644 --- a/cpp/src/qpid/broker/Fairshare.cpp +++ b/cpp/src/qpid/broker/Fairshare.cpp @@ -19,7 +19,8 @@ * */ #include "qpid/broker/Fairshare.h" -#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/QueueSettings.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" @@ -83,17 +84,6 @@ bool Fairshare::setState(uint p, uint c) return true; } -bool Fairshare::findFrontLevel(uint& p, PriorityLevels& messages) -{ - const uint start = p = currentLevel(); - do { - if (!messages[p].empty()) return true; - } while ((p = nextLevel()) != start); - return false; -} - - - bool Fairshare::getState(const Messages& m, uint& priority, uint& count) { const Fairshare* fairshare = dynamic_cast<const Fairshare*>(&m); @@ -106,82 +96,30 @@ bool Fairshare::setState(Messages& m, uint priority, uint count) return fairshare && fairshare->setState(priority, count); } -int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys) -{ - qpid::framing::FieldTable::ValuePtr v; - std::vector<std::string>::const_iterator i = keys.begin(); - while (!v && i != keys.end()) { - v = settings.get(*i++); - } - - if (!v) { - return 0; - } else if (v->convertsTo<int>()) { - return v->get<int>(); - } else if (v->convertsTo<std::string>()){ - std::string s = v->get<std::string>(); - try { - return boost::lexical_cast<int>(s); - } catch(const boost::bad_lexical_cast&) { - QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << s); - return 0; - } - } else { - QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << *v); - return 0; - } -} - -int getIntegerSettingForKey(const qpid::framing::FieldTable& settings, const std::string& key) -{ - return getIntegerSetting(settings, boost::assign::list_of<std::string>(key)); -} - -int getSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys, int minvalue, int maxvalue) +PriorityQueue::Priority Fairshare::firstLevel() { - return std::max(minvalue,std::min(getIntegerSetting(settings, keys), maxvalue)); + return Priority(currentLevel()); } -std::auto_ptr<Fairshare> getFairshareForKey(const qpid::framing::FieldTable& settings, uint levels, const std::string& key) +bool Fairshare::nextLevel(Priority& p) { - uint defaultLimit = getIntegerSettingForKey(settings, key); - std::auto_ptr<Fairshare> fairshare(new Fairshare(levels, defaultLimit)); - for (uint i = 0; i < levels; i++) { - std::string levelKey = (boost::format("%1%-%2%") % key % i).str(); - if(settings.isSet(levelKey)) { - fairshare->setLimit(i, getIntegerSettingForKey(settings, levelKey)); - } - } - if (!fairshare->isNull()) { - return fairshare; + int next = nextLevel(); + if (next == p.start) { + return false; } else { - return std::auto_ptr<Fairshare>(); - } -} - -std::auto_ptr<Fairshare> getFairshare(const qpid::framing::FieldTable& settings, - uint levels, - const std::vector<std::string>& keys) -{ - std::auto_ptr<Fairshare> fairshare; - for (std::vector<std::string>::const_iterator i = keys.begin(); i != keys.end() && !fairshare.get(); ++i) { - fairshare = getFairshareForKey(settings, levels, *i); + p.current = next; + return true; } - return fairshare; } -std::auto_ptr<Messages> Fairshare::create(const qpid::framing::FieldTable& settings) +std::auto_ptr<Messages> Fairshare::create(const QueueSettings& settings) { - using boost::assign::list_of; - std::auto_ptr<Messages> result; - size_t levels = getSetting(settings, list_of<std::string>("qpid.priorities")("x-qpid-priorities"), 0, 100); - if (levels) { - std::auto_ptr<Fairshare> fairshare = - getFairshare(settings, levels, list_of<std::string>("qpid.fairshare")("x-qpid-fairshare")); - if (fairshare.get()) result = fairshare; - else result = std::auto_ptr<Messages>(new PriorityQueue(levels)); + std::auto_ptr<Fairshare> fairshare(new Fairshare(settings.priorities, settings.defaultFairshare)); + for (uint i = 0; i < settings.priorities; i++) { + std::map<uint32_t,uint32_t>::const_iterator l = settings.fairshare.find(i); + if (l != settings.fairshare.end()) fairshare->setLimit(i, l->second); } - return result; + return std::auto_ptr<Messages>(fairshare.release()); } }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Fairshare.h b/cpp/src/qpid/broker/Fairshare.h index 1b25721e0c..e7c8b04ecf 100644 --- a/cpp/src/qpid/broker/Fairshare.h +++ b/cpp/src/qpid/broker/Fairshare.h @@ -24,13 +24,11 @@ #include "qpid/broker/PriorityQueue.h" namespace qpid { -namespace framing { -class FieldTable; -} namespace broker { +struct QueueSettings; /** - * Modifies a basic prioirty queue by limiting the number of messages + * Modifies a basic priority queue by limiting the number of messages * from each priority level that are dispatched before allowing * dispatch from the next level. */ @@ -42,7 +40,7 @@ class Fairshare : public PriorityQueue bool setState(uint priority, uint count); void setLimit(size_t level, uint limit); bool isNull(); - static std::auto_ptr<Messages> create(const qpid::framing::FieldTable& settings); + static std::auto_ptr<Messages> create(const QueueSettings& settings); static bool getState(const Messages&, uint& priority, uint& count); static bool setState(Messages&, uint priority, uint count); private: @@ -54,7 +52,8 @@ class Fairshare : public PriorityQueue uint currentLevel(); uint nextLevel(); bool limitReached(); - bool findFrontLevel(uint& p, PriorityLevels&); + Priority firstLevel(); + bool nextLevel(Priority& ); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/FifoDistributor.cpp b/cpp/src/qpid/broker/FifoDistributor.cpp index c9ba894297..e1c0d268ce 100644 --- a/cpp/src/qpid/broker/FifoDistributor.cpp +++ b/cpp/src/qpid/broker/FifoDistributor.cpp @@ -28,21 +28,14 @@ using namespace qpid::broker; FifoDistributor::FifoDistributor(Messages& container) : messages(container) {} -bool FifoDistributor::nextConsumableMessage( Consumer::shared_ptr&, QueuedMessage& next ) +bool FifoDistributor::acquire(const std::string&, Message& msg) { - return messages.consume(next); -} - -bool FifoDistributor::allocate(const std::string&, const QueuedMessage& ) -{ - // by default, all messages present on the queue may be allocated as they have yet to - // be acquired. - return true; -} - -bool FifoDistributor::nextBrowsableMessage( Consumer::shared_ptr& c, QueuedMessage& next ) -{ - return messages.browse(c->getPosition(), next, !c->browseAcquired()); + if (msg.getState() == AVAILABLE) { + msg.setState(ACQUIRED); + return true; + } else { + return false; + } } void FifoDistributor::query(qpid::types::Variant::Map&) const diff --git a/cpp/src/qpid/broker/FifoDistributor.h b/cpp/src/qpid/broker/FifoDistributor.h index 245537ed12..aa5ebe28c5 100644 --- a/cpp/src/qpid/broker/FifoDistributor.h +++ b/cpp/src/qpid/broker/FifoDistributor.h @@ -38,15 +38,7 @@ class FifoDistributor : public MessageDistributor public: FifoDistributor(Messages& container); - /** Locking Note: all methods assume the caller is holding the Queue::messageLock - * during the method call. - */ - - /** MessageDistributor interface */ - - bool nextConsumableMessage( Consumer::shared_ptr& consumer, QueuedMessage& next ); - bool allocate(const std::string& consumer, const QueuedMessage& target); - bool nextBrowsableMessage( Consumer::shared_ptr& consumer, QueuedMessage& next ); + bool acquire(const std::string& consumer, Message& target); void query(qpid::types::Variant::Map&) const; private: diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp index 9975d26c72..02c05852ff 100644 --- a/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/cpp/src/qpid/broker/HeadersExchange.cpp @@ -19,6 +19,7 @@ * */ #include "qpid/broker/HeadersExchange.h" +#include "qpid/broker/MapHandler.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" @@ -55,6 +56,100 @@ namespace { const std::string fedOpUnbind("U"); const std::string fedOpReorigin("R"); const std::string fedOpHello("H"); + +std::string getMatch(const FieldTable* args) +{ + if (!args) { + throw InternalErrorException(QPID_MSG("No arguments given.")); + } + FieldTable::ValuePtr what = args->get(x_match); + if (!what) { + return empty; + } + if (!what->convertsTo<std::string>()) { + throw InternalErrorException(QPID_MSG("Invalid x-match binding format to headers exchange. Must be a string [\"all\" or \"any\"]")); + } + return what->get<std::string>(); +} +class Matcher : public MapHandler +{ + public: + Matcher(const FieldTable& b) : binding(b), matched(0) {} + void handleUint8(const MapHandler::CharSequence& key, uint8_t value) { processUint(std::string(key.data, key.size), value); } + void handleUint16(const MapHandler::CharSequence& key, uint16_t value) { processUint(std::string(key.data, key.size), value); } + void handleUint32(const MapHandler::CharSequence& key, uint32_t value) { processUint(std::string(key.data, key.size), value); } + void handleUint64(const MapHandler::CharSequence& key, uint64_t value) { processUint(std::string(key.data, key.size), value); } + void handleInt8(const MapHandler::CharSequence& key, int8_t value) { processInt(std::string(key.data, key.size), value); } + void handleInt16(const MapHandler::CharSequence& key, int16_t value) { processInt(std::string(key.data, key.size), value); } + void handleInt32(const MapHandler::CharSequence& key, int32_t value) { processInt(std::string(key.data, key.size), value); } + void handleInt64(const MapHandler::CharSequence& key, int64_t value) { processInt(std::string(key.data, key.size), value); } + void handleFloat(const MapHandler::CharSequence& key, float value) { processFloat(std::string(key.data, key.size), value); } + void handleDouble(const MapHandler::CharSequence& key, double value) { processFloat(std::string(key.data, key.size), value); } + void handleString(const MapHandler::CharSequence& key, const MapHandler::CharSequence& value, const MapHandler::CharSequence& /*encoding*/) + { + processString(std::string(key.data, key.size), std::string(value.data, value.size)); + } + void handleVoid(const MapHandler::CharSequence& key) + { + valueCheckRequired(std::string(key.data, key.size)); + } + bool matches() + { + std::string what = getMatch(&binding); + if (what == all) { + //must match all entries in the binding, except the match mode indicator + return matched == binding.size() - 1; + } else if (what == any) { + //match any of the entries in the binding + return matched > 0; + } else { + return false; + } + } + private: + bool valueCheckRequired(const std::string& key) + { + FieldTable::ValuePtr v = binding.get(key); + if (v) { + if (v->getType() == 0xf0/*VOID*/) { + ++matched; + return false; + } else { + return true; + } + } else { + return false; + } + } + + void processString(const std::string& key, const std::string& actual) + { + if (valueCheckRequired(key) && binding.getAsString(key) == actual) { + ++matched; + } + } + void processFloat(const std::string& key, double actual) + { + double bound; + if (valueCheckRequired(key) && binding.getDouble(key, bound) && bound == actual) { + ++matched; + } + } + void processInt(const std::string& key, int64_t actual) + { + if (valueCheckRequired(key) && binding.getAsInt64(key) == actual) { + ++matched; + } + } + void processUint(const std::string& key, uint64_t actual) + { + if (valueCheckRequired(key) && binding.getAsUInt64(key) == actual) { + ++matched; + } + } + const FieldTable& binding; + size_t matched; +}; } HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent, Broker* b) : @@ -72,21 +167,6 @@ HeadersExchange::HeadersExchange(const std::string& _name, bool _durable, mgmtExchange->set_type (typeName); } -std::string HeadersExchange::getMatch(const FieldTable* args) -{ - if (!args) { - throw InternalErrorException(QPID_MSG("No arguments given.")); - } - FieldTable::ValuePtr what = args->get(x_match); - if (!what) { - return empty; - } - if (!what->convertsTo<std::string>()) { - throw InternalErrorException(QPID_MSG("Invalid x-match binding format to headers exchange. Must be a string [\"all\" or \"any\"]")); - } - return what->get<std::string>(); -} - bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args) { string fedOp(fedOpBind); @@ -196,28 +276,16 @@ bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, 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) { - mgmtExchange->inc_msgReceives(); - mgmtExchange->inc_byteReceives(msg.contentSize()); - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsNoRoute(); - } - return; - } - PreRoute pr(msg, this); BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >); Bindings::ConstPtr p = bindings.snapshot(); if (p.get()) { for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) { - if (match((*i).binding->args, *args)) { - b->push_back((*i).binding); + Matcher matcher(i->binding->args); + msg.getMessage().processProperties(matcher); + if (matcher.matches()) { + b->push_back(i->binding); } } } diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h index d10892b9cc..2e4669a018 100644 --- a/cpp/src/qpid/broker/HeadersExchange.h +++ b/cpp/src/qpid/broker/HeadersExchange.h @@ -73,9 +73,6 @@ class HeadersExchange : public virtual Exchange { Bindings bindings; qpid::sys::Mutex lock; - - static std::string getMatch(const framing::FieldTable* args); - protected: void getNonFedArgs(const framing::FieldTable* args, framing::FieldTable& nonFedArgs); diff --git a/cpp/src/qpid/broker/IndexedDeque.h b/cpp/src/qpid/broker/IndexedDeque.h new file mode 100644 index 0000000000..229b4e3009 --- /dev/null +++ b/cpp/src/qpid/broker/IndexedDeque.h @@ -0,0 +1,226 @@ +#ifndef QPID_BROKER_INDEXEDDEQUE_H +#define QPID_BROKER_INDEXEDDEQUE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/framing/SequenceNumber.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Messages.h" +#include "qpid/broker/QueueCursor.h" +#include "qpid/log/Statement.h" +#include <deque> + +namespace qpid { +namespace broker { + +/** + * Template for a deque whose contents can be refered to by + * QueueCursor + */ +template <typename T> class IndexedDeque +{ + public: + typedef boost::function1<T, qpid::framing::SequenceNumber> Padding; + IndexedDeque(Padding p) : head(0), version(0), padding(p) {} + + bool index(const QueueCursor& cursor, size_t& result) + { + return cursor.valid && index(qpid::framing::SequenceNumber(cursor.position + 1), result); + } + + /** + * Finds the index for the message with the specified sequence number. + * + * @returns true if a message was found with the specified sequence, + * in which case the second parameter will be set to the index of that + * message; false if no message with that sequence exists, in which + * case the second parameter will be 0 if the sequence is less than + * that of the first message and non-zero if it is greater than that + * of the last message + */ + bool index(const qpid::framing::SequenceNumber& position, size_t& i) + { + //assuming a monotonic sequence, with no messages removed except + //from the ends of the deque, we can use the position to determine + //an index into the deque + if (messages.size()) { + qpid::framing::SequenceNumber front(messages.front().getSequence()); + if (position < front) { + i = 0; + } else { + i = position - front; + return i < messages.size(); + } + } + return false; + } + + bool deleted(const QueueCursor& cursor) + { + size_t i; + if (cursor.valid && index(cursor.position, i)) { + messages[i].setState(DELETED); + clean(); + return true; + } else { + return false; + } + } + + T& publish(const T& added) + { + // QPID-4046: let producer help clean the backlog of deleted messages + clean(); + //for ha replication, the queue can sometimes be reset by + //removing some of the more recent messages, in this case we + //need to ensure the DELETED records at the tail do not interfere with indexing + while (messages.size() && added.getSequence() <= messages.back().getSequence() && messages.back().getState() == DELETED) + messages.pop_back(); + if (messages.size() && added.getSequence() <= messages.back().getSequence()) throw qpid::Exception(QPID_MSG("Index out of sequence!")); + + //add padding to prevent gaps in sequence, which break the index + //calculation (needed for queue replication) + while (messages.size() && (added.getSequence() - messages.back().getSequence()) > 1) + messages.push_back(padding(messages.back().getSequence() + 1)); + + messages.push_back(added); + T& m = messages.back(); + m.setState(AVAILABLE); + if (head >= messages.size()) head = messages.size() - 1; + QPID_LOG(debug, "Message " << &m << " published, state is " << m.getState() << " (head is now " << head << ")"); + return m; + } + + T* release(const QueueCursor& cursor) + { + size_t i; + if (cursor.valid && index(cursor.position, i)) { + messages[i].setState(AVAILABLE); + ++version; + QPID_LOG(debug, "Released message at position " << cursor.position << ", index " << i); + return &messages[i]; + } else { + if (!cursor.valid) { QPID_LOG(debug, "Could not release message; cursor was invalid");} + else { QPID_LOG(debug, "Could not release message at position " << cursor.position); } + return 0; + } + } + + bool reset(const QueueCursor& cursor) + { + return !cursor.valid || (cursor.type == CONSUMER && cursor.version != version); + } + + T* next(QueueCursor& cursor) + { + size_t i = 0; + if (reset(cursor)) i = head; //start from head + else index(cursor, i); //get first message that is greater than position + + if (cursor.valid) { + QPID_LOG(debug, "next() called for cursor at " << cursor.position << ", index set to " << i << " (of " << messages.size() << ")"); + } else { + QPID_LOG(debug, "next() called for invalid cursor, index started at " << i << " (of " << messages.size() << ")"); + } + while (i < messages.size()) { + T& m = messages[i++]; + if (m.getState() == DELETED) continue; + cursor.setPosition(m.getSequence(), version); + QPID_LOG(debug, "in next(), cursor set to " << cursor.position); + + if (cursor.check(m)) { + QPID_LOG(debug, "in next(), returning message at " << cursor.position); + return &m; + } + } + QPID_LOG(debug, "no message to return from next"); + return 0; + } + + size_t size() + { + size_t count(0); + for (size_t i = head; i < messages.size(); ++i) { + if (messages[i].getState() == AVAILABLE) ++count; + } + return count; + } + + T* find(const qpid::framing::SequenceNumber& position, QueueCursor* cursor) + { + size_t i = 0; + if (index(position, i)){ + T& m = messages[i]; + if (cursor) cursor->setPosition(position, version); + if (m.getState() == AVAILABLE || m.getState() == ACQUIRED) { + return &m; + } + } else if (cursor) { + if (i >= messages.size()) cursor->setPosition(position, version);//haven't yet got a message with that seq no + else if (i == 0) cursor->valid = false;//reset + } + return 0; + } + + T* find(const QueueCursor& cursor) + { + if (cursor.valid) return find(cursor.position, 0); + else return 0; + } + + void clean() + { + // QPID-4046: If a queue has multiple consumers, then it is possible for a large + // collection of deleted messages to build up. Limit the number of messages cleaned + // up on each call to clean(). + size_t count = 0; + while (messages.size() && messages.front().getState() == DELETED && count < 10) { + messages.pop_front(); + count += 1; + } + head = (head > count) ? head - count : 0; + QPID_LOG(debug, "clean(): " << messages.size() << " messages remain; head is now " << head); + } + + void foreach(Messages::Functor f) + { + for (typename Deque::iterator i = messages.begin(); i != messages.end(); ++i) { + if (i->getState() == AVAILABLE) { + f(*i); + } + } + clean(); + } + + void resetCursors() + { + ++version; + } + + typedef std::deque<T> Deque; + Deque messages; + size_t head; + int32_t version; + Padding padding; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_INDEXEDDEQUE_H*/ diff --git a/cpp/src/qpid/broker/LegacyLVQ.cpp b/cpp/src/qpid/broker/LegacyLVQ.cpp index f1deddf4c8..e69de29bb2 100644 --- a/cpp/src/qpid/broker/LegacyLVQ.cpp +++ b/cpp/src/qpid/broker/LegacyLVQ.cpp @@ -1,127 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/broker/LegacyLVQ.h" -#include "qpid/broker/Broker.h" -#include "qpid/broker/QueuedMessage.h" - -namespace qpid { -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 && i->second.status == QueuedMessage::AVAILABLE) { - i->second.status = QueuedMessage::ACQUIRED; - message = i->second; - return true; - } else { - return false; - } -} - -bool LegacyLVQ::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) -{ - if (MessageMap::browse(position, message, unacquired)) { - if (!noBrowse) index.erase(getKey(message)); - return true; - } else { - return false; - } -} - -bool LegacyLVQ::push(const QueuedMessage& added, QueuedMessage& removed) -{ - //Hack to disable LVQ behaviour on cluster update: - if (broker && broker->isClusterUpdatee()) { - messages[added.position] = added; - return false; - } else { - return MessageMap::push(added, 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); - 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) -{ - //Note: This method is currently called periodically on the timer - //thread to expire messages. In a clustered broker this means that - //the purging does not occur on the cluster event dispatch thread - //and consequently that is not totally ordered w.r.t other events - //(including publication of messages). The cluster does ensure - //that the actual expiration of messages (as distinct from the - //removing of those expired messages from the queue) *is* - //consistently ordered w.r.t. cluster events. This means that - //delivery of messages is in general consistent across the cluster - //inspite of any non-determinism in the triggering of a - //purge. However at present purging a last value queue (of the - //legacy sort) could potentially cause inconsistencies in the - //cluster (as the order w.r.t publications can affect the order in - //which messages appear in the queue). Consequently periodic - //purging of an LVQ is not enabled if the broker is clustered - //(expired messages will be removed on delivery and consolidated - //by key as part of normal LVQ operation). - if (!broker || !broker->isInCluster()) - MessageMap::removeIf(p); -} - -std::auto_ptr<Messages> LegacyLVQ::updateOrReplace(std::auto_ptr<Messages> current, - const std::string& key, bool noBrowse, Broker* broker) -{ - LegacyLVQ* lvq = dynamic_cast<LegacyLVQ*>(current.get()); - if (lvq) { - lvq->setNoBrowse(noBrowse); - return current; - } else { - return std::auto_ptr<Messages>(new LegacyLVQ(key, noBrowse, broker)); - } -} - -}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/LegacyLVQ.h b/cpp/src/qpid/broker/LegacyLVQ.h index 9355069f37..e69de29bb2 100644 --- a/cpp/src/qpid/broker/LegacyLVQ.h +++ b/cpp/src/qpid/broker/LegacyLVQ.h @@ -1,60 +0,0 @@ -#ifndef QPID_BROKER_LEGACYLVQ_H -#define QPID_BROKER_LEGACYLVQ_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/MessageMap.h" -#include <memory> - -namespace qpid { -namespace broker { -class Broker; - -/** - * This class encapsulates the behaviour of the old style LVQ where a - * message replacing another messages for the given key will use the - * position in the queue of the previous message. This however causes - * problems for browsing. Either browsers stop the coalescing of - * messages by key (default) or they may mis updates (if the no-browse - * option is specified). - */ -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); - void removeIf(Predicate); - void setNoBrowse(bool); - static std::auto_ptr<Messages> updateOrReplace(std::auto_ptr<Messages> current, - const std::string& key, bool noBrowse, - Broker* broker); - protected: - bool noBrowse; - Broker* broker; - - const QueuedMessage& replace(const QueuedMessage&, const QueuedMessage&); -}; -}} // namespace qpid::broker - -#endif /*!QPID_BROKER_LEGACYLVQ_H*/ diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp index 84dd163ac3..6479e47799 100644 --- a/cpp/src/qpid/broker/Link.cpp +++ b/cpp/src/qpid/broker/Link.cpp @@ -92,10 +92,10 @@ public: // 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) + void route(broker::Deliverable& /*msg*/) { if (!link) return; - const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders(); + const framing::FieldTable* headers = 0;//TODO: 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(): diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp index 0507fe6521..6f813554fa 100644 --- a/cpp/src/qpid/broker/LinkRegistry.cpp +++ b/cpp/src/qpid/broker/LinkRegistry.cpp @@ -119,6 +119,7 @@ pair<Link::shared_ptr, bool> LinkRegistry::declare(const string& name, parent, failover)); if (durable && store) store->create(*link); links[name] = link; + pendingLinks[name] = link; QPID_LOG(debug, "Creating new link; name=" << name ); return std::pair<Link::shared_ptr, bool>(link, true); } @@ -229,6 +230,7 @@ void LinkRegistry::linkDestroyed(Link *link) QPID_LOG(debug, "LinkRegistry::destroy(); link= " << link->getName()); Mutex::ScopedLock locker(lock); + pendingLinks.erase(link->getName()); LinkMap::iterator i = links.find(link->getName()); if (i != links.end()) { @@ -322,10 +324,12 @@ void LinkRegistry::notifyConnection(const std::string& key, Connection* c) Link::shared_ptr link; { Mutex::ScopedLock locker(lock); - for (LinkMap::iterator l = links.begin(); l != links.end(); ++l) { + for (LinkMap::iterator l = pendingLinks.begin(); l != pendingLinks.end(); ++l) { if (l->second->pendingConnection(host, port)) { link = l->second; + pendingLinks.erase(l); connections[key] = link->getName(); + QPID_LOG(debug, "LinkRegistry:: found pending =" << link->getName()); break; } } @@ -347,6 +351,10 @@ void LinkRegistry::notifyClosed(const std::string& key) { Link::shared_ptr link = findLink(key); if (link) { + { + Mutex::ScopedLock locker(lock); + pendingLinks[link->getName()] = link; + } link->closed(0, "Closed by peer"); } } @@ -355,6 +363,10 @@ void LinkRegistry::notifyConnectionForced(const std::string& key, const std::str { Link::shared_ptr link = findLink(key); if (link) { + { + Mutex::ScopedLock locker(lock); + pendingLinks[link->getName()] = link; + } link->notifyConnectionForced(text); } } diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h index 5a39b62bd1..076ab831c9 100644 --- a/cpp/src/qpid/broker/LinkRegistry.h +++ b/cpp/src/qpid/broker/LinkRegistry.h @@ -47,6 +47,7 @@ namespace broker { LinkMap links; /** indexed by name of Link */ BridgeMap bridges; /** indexed by name of Bridge */ ConnectionMap connections; /** indexed by connection identifier, gives link name */ + LinkMap pendingLinks; /** pending connection, indexed by name of Link */ qpid::sys::Mutex lock; Broker* broker; diff --git a/cpp/src/qpid/broker/LossyQueue.cpp b/cpp/src/qpid/broker/LossyQueue.cpp new file mode 100644 index 0000000000..ee2c3ca794 --- /dev/null +++ b/cpp/src/qpid/broker/LossyQueue.cpp @@ -0,0 +1,86 @@ +/* + * + * 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 "LossyQueue.h" +#include "QueueDepth.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { + +namespace { +bool isLowerPriorityThan(uint8_t priority, const Message& m) +{ + return m.getPriority() <= priority; +} +} + +LossyQueue::LossyQueue(const std::string& n, const QueueSettings& s, MessageStore* const ms, management::Manageable* p, Broker* b) + : Queue(n, s, ms, p, b) {} + +bool LossyQueue::checkDepth(const QueueDepth& increment, const Message& message) +{ + if (increment.getSize() > settings.maxDepth.getSize()) { + if (mgmtObject) { + mgmtObject->inc_discardsOverflow(); + if (brokerMgmtObject) + brokerMgmtObject->inc_discardsOverflow(); + } + throw qpid::framing::ResourceLimitExceededException(QPID_MSG("Message larger than configured maximum depth on " + << name << ": size=" << increment.getSize() << ", max-size=" << settings.maxDepth.getSize())); + } + + while (settings.maxDepth && (current + increment > settings.maxDepth)) { + QPID_LOG(debug, "purging " << name << ": current depth is [" << current << "], max depth is [" << settings.maxDepth << "], new message has size " << increment.getSize()); + qpid::sys::Mutex::ScopedUnlock u(messageLock); + //TODO: arguably we should try and purge expired messages first but that is potentially expensive + if (remove(1, settings.priorities ? boost::bind(&isLowerPriorityThan, message.getPriority(), _1) : MessagePredicate(), MessageFunctor(), PURGE)) { + if (mgmtObject) { + mgmtObject->inc_discardsRing(1); + if (brokerMgmtObject) + brokerMgmtObject->inc_discardsRing(1); + } + } else { + //should only be the case for a non-empty queue if we are + //testing priority and there was no lower (or equal) + //priority message available to purge + break; + } + } + if (settings.maxDepth && (current + increment > settings.maxDepth)) { + //will only be the case where we were unable to purge another + //message, which should only be the case if we are purging + //based on priority and there was no message with a lower (or + //equal) priority than this one, meaning that we drop this + //current message + if (mgmtObject) { + mgmtObject->inc_discardsRing(1); + if (brokerMgmtObject) + brokerMgmtObject->inc_discardsRing(1); + } + return false; + } else { + //have sufficient space for this message + current += increment; + return true; + } +} +}} // namespace qpid::broker diff --git a/cpp/src/qpid/cluster/Quorum_null.h b/cpp/src/qpid/broker/LossyQueue.h index dc27f0a43b..3e62151d6f 100644 --- a/cpp/src/qpid/cluster/Quorum_null.h +++ b/cpp/src/qpid/broker/LossyQueue.h @@ -1,5 +1,5 @@ -#ifndef QPID_CLUSTER_QUORUM_NULL_H -#define QPID_CLUSTER_QUORUM_NULL_H +#ifndef QPID_BROKER_LOSSYQUEUE_H +#define QPID_BROKER_LOSSYQUEUE_H /* * @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -21,22 +21,21 @@ * under the License. * */ - -#include <boost/shared_ptr.hpp> -#include <boost/function.hpp> +#include "qpid/broker/Queue.h" namespace qpid { -namespace cluster { -class Cluster; +namespace broker { -/** Null implementation of quorum. */ - -class Quorum { +/** + * Drops messages to prevent a breach of any configured maximum depth. + */ +class LossyQueue : public Queue +{ public: - Quorum(boost::function<void ()>) {} - void start(boost::shared_ptr<sys::Poller>) {} + LossyQueue(const std::string&, const QueueSettings&, MessageStore* const, management::Manageable*, Broker*); + bool checkDepth(const QueueDepth& increment, const Message&); + private: }; +}} // namespace qpid::broker -#endif /*!QPID_CLUSTER_QUORUM_NULL_H*/ - -}} // namespace qpid::cluster +#endif /*!QPID_BROKER_LOSSYQUEUE_H*/ diff --git a/cpp/src/qpid/broker/Lvq.cpp b/cpp/src/qpid/broker/Lvq.cpp new file mode 100644 index 0000000000..d053616c8a --- /dev/null +++ b/cpp/src/qpid/broker/Lvq.cpp @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Lvq.h" +#include "MessageMap.h" +#include "qpid/sys/ClusterSafe.h" +#include "qpid/sys/Monitor.h" + +namespace qpid { +namespace broker { +Lvq::Lvq(const std::string& n, std::auto_ptr<MessageMap> m, const QueueSettings& s, MessageStore* const ms, management::Manageable* p, Broker* b) + : Queue(n, s, ms, p, b), messageMap(*m) +{ + messages = m; +} + +void Lvq::push(Message& message, bool isRecovery) +{ + qpid::sys::assertClusterSafe(); + QueueListeners::NotificationSet copy; + Message old; + bool removed; + { + qpid::sys::Mutex::ScopedLock locker(messageLock); + message.setSequence(++sequence); + removed = messageMap.update(message, old); + listeners.populate(copy); + observeEnqueue(message, locker); + if (removed) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + mgmtObject->inc_discardsLvq(); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(); + brokerMgmtObject->inc_discardsLvq(); + } + } + observeDequeue(old, locker); + } + } + copy.notify(); + if (removed) { + if (isRecovery) pendingDequeues.push_back(old); + else dequeueFromStore(old.getPersistentContext());//do outside of lock + } +} +}} // namespace qpid::broker diff --git a/cpp/src/qpid/cluster/ExpiryPolicy.h b/cpp/src/qpid/broker/Lvq.h index d8ddbca8b3..335270a073 100644 --- a/cpp/src/qpid/cluster/ExpiryPolicy.h +++ b/cpp/src/qpid/broker/Lvq.h @@ -1,5 +1,5 @@ -#ifndef QPID_CLUSTER_EXPIRYPOLICY_H -#define QPID_CLUSTER_EXPIRYPOLICY_H +#ifndef QPID_BROKER_LVQ_H +#define QPID_BROKER_LVQ_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,39 +21,25 @@ * under the License. * */ - -#include "qpid/cluster/types.h" -#include "qpid/broker/ExpiryPolicy.h" -#include "qpid/sys/Mutex.h" -#include <boost/function.hpp> -#include <boost/intrusive_ptr.hpp> -#include <boost/optional.hpp> -#include <map> +#include "qpid/broker/Queue.h" namespace qpid { - namespace broker { -class Message; -} - -namespace cluster { -class Cluster; +class MessageMap; /** - * Cluster expiry policy + * Subclass of queue that handles last-value-queue semantics in + * conjunction with the MessageMap class. This requires an existing + * message to be 'replaced' by a newer message with the same key. */ -class ExpiryPolicy : public broker::ExpiryPolicy +class Lvq : public Queue { public: - ExpiryPolicy(Cluster& cluster); - - bool hasExpired(broker::Message&); - qpid::sys::AbsTime getCurrentTime(); - + Lvq(const std::string&, std::auto_ptr<MessageMap>, const QueueSettings&, MessageStore* const, management::Manageable*, Broker*); + void push(Message& msg, bool isRecovery=false); private: - Cluster& cluster; + MessageMap& messageMap; }; +}} // namespace qpid::broker -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_EXPIRYPOLICY_H*/ +#endif /*!QPID_BROKER_LVQ_H*/ diff --git a/cpp/src/qpid/broker/MapHandler.h b/cpp/src/qpid/broker/MapHandler.h new file mode 100644 index 0000000000..200eba4f7a --- /dev/null +++ b/cpp/src/qpid/broker/MapHandler.h @@ -0,0 +1,57 @@ +#ifndef QPID_BROKER_MAPHANDLER_H +#define QPID_BROKER_MAPHANDLER_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/IntegerTypes.h" + +namespace qpid { +namespace broker { + +/** + * Interface for processing entries in some map-like object + */ +class MapHandler +{ + public: + typedef struct { + const char* data; + size_t size; + } CharSequence; + + virtual ~MapHandler() {} + virtual void handleVoid(const CharSequence& key) = 0; + virtual void handleUint8(const CharSequence& key, uint8_t value) = 0; + virtual void handleUint16(const CharSequence& key, uint16_t value) = 0; + virtual void handleUint32(const CharSequence& key, uint32_t value) = 0; + virtual void handleUint64(const CharSequence& key, uint64_t value) = 0; + virtual void handleInt8(const CharSequence& key, int8_t value) = 0; + virtual void handleInt16(const CharSequence& key, int16_t value) = 0; + virtual void handleInt32(const CharSequence& key, int32_t value) = 0; + virtual void handleInt64(const CharSequence& key, int64_t value) = 0; + virtual void handleFloat(const CharSequence& key, float value) = 0; + virtual void handleDouble(const CharSequence& key, double value) = 0; + virtual void handleString(const CharSequence& key, const CharSequence& value, const CharSequence& encoding) = 0; + private: +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_MAPHANDLER_H*/ diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp index 4dd8a349dd..c48e9bcfa4 100644 --- a/cpp/src/qpid/broker/Message.cpp +++ b/cpp/src/qpid/broker/Message.cpp @@ -20,19 +20,12 @@ */ #include "qpid/broker/Message.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/ExchangeRegistry.h" -#include "qpid/broker/ExpiryPolicy.h" +#include "qpid/broker/MapHandler.h" #include "qpid/StringUtils.h" -#include "qpid/framing/frame_functors.h" -#include "qpid/framing/FieldTable.h" -#include "qpid/framing/MessageTransferBody.h" -#include "qpid/framing/SendContent.h" -#include "qpid/framing/SequenceNumber.h" -#include "qpid/framing/TypeFilter.h" -#include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" +#include <algorithm> +#include <string.h> #include <time.h> using boost::intrusive_ptr; @@ -41,492 +34,261 @@ using qpid::sys::Duration; using qpid::sys::TIME_MSEC; using qpid::sys::FAR_FUTURE; using std::string; -using namespace qpid::framing; namespace qpid { namespace broker { -TransferAdapter Message::TRANSFER; - -Message::Message(const framing::SequenceNumber& id) : - frames(id), persistenceId(0), redelivered(false), loaded(false), - staged(false), forcePersistentPolicy(false), publisher(0), adapter(0), - expiration(FAR_FUTURE), dequeueCallback(0), - inCallback(false), requiredCredit(0), isManagementMessage(false), copyHeaderOnWrite(false) -{} - -Message::~Message() {} - -void Message::forcePersistent() +Message::Message() : deliveryCount(0), publisher(0), expiration(FAR_FUTURE), timestamp(0), isManagementMessage(false) {} +Message::Message(boost::intrusive_ptr<Encoding> e, boost::intrusive_ptr<PersistableMessage> p) + : encoding(e), persistentContext(p), deliveryCount(0), publisher(0), expiration(FAR_FUTURE), timestamp(0), isManagementMessage(false) { - sys::Mutex::ScopedLock l(lock); - // only set forced bit if we actually need to force. - if (! getAdapter().isPersistent(frames) ){ - forcePersistentPolicy = true; - } + if (persistentContext) persistentContext->setIngressCompletion(e); } +Message::~Message() {} -bool Message::isForcedPersistent() -{ - return forcePersistentPolicy; -} std::string Message::getRoutingKey() const { - return getAdapter().getRoutingKey(frames); -} - -std::string Message::getExchangeName() const -{ - return getAdapter().getExchange(frames); -} - -const boost::shared_ptr<Exchange> Message::getExchange(ExchangeRegistry& registry) const -{ - if (!exchange) { - exchange = registry.get(getExchangeName()); - } - return exchange; -} - -bool Message::isImmediate() const -{ - return getAdapter().isImmediate(frames); -} - -const FieldTable* Message::getApplicationHeaders() const -{ - sys::Mutex::ScopedLock l(lock); - return getAdapter().getApplicationHeaders(frames); -} - -std::string Message::getAppId() const -{ - sys::Mutex::ScopedLock l(lock); - return getAdapter().getAppId(frames); + return getEncoding().getRoutingKey(); } bool Message::isPersistent() const { - sys::Mutex::ScopedLock l(lock); - return (getAdapter().isPersistent(frames) || forcePersistentPolicy); + return getEncoding().isPersistent(); } -bool Message::requiresAccept() +uint64_t Message::getContentSize() const { - return getAdapter().requiresAccept(frames); + return getEncoding().getContentSize(); } -uint32_t Message::getRequiredCredit() +boost::intrusive_ptr<AsyncCompletion> Message::getIngressCompletion() const { - sys::Mutex::ScopedLock l(lock); - if (!requiredCredit) { - //add up payload for all header and content frames in the frameset - SumBodySize sum; - frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>()); - requiredCredit = sum.getSize(); - } - return requiredCredit; + return encoding; } -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>()); - - //then encode the payload of each content frame - framing::EncodeBody f2(buffer); - frames.map_if(f2, TypeFilter<CONTENT_BODY>()); -} - -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>()); -} - -uint32_t Message::encodedSize() const -{ - return encodedHeaderSize() + encodedContentSize(); -} - -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>()); - return sum.getSize(); -} - -void Message::decodeHeader(framing::Buffer& buffer) +namespace { - AMQFrame method; - method.decode(buffer); - frames.append(method); - - AMQFrame header; - header.decode(buffer); - frames.append(header); +const std::string X_QPID_TRACE("x-qpid.trace"); } -void Message::decodeContent(framing::Buffer& buffer) +bool Message::isExcluded(const std::vector<std::string>& excludes) const { - if (buffer.available()) { - //get the data as a string and set that as the content - //body on a frame then add that frame to the frameset - AMQFrame frame((AMQContentBody())); - frame.castBody<AMQContentBody>()->decode(buffer, buffer.available()); - frame.setFirstSegment(false); - frames.append(frame); - } else { - //adjust header flags - MarkLastSegment f; - frames.map_if(f, TypeFilter<HEADER_BODY>()); + std::string traceStr = getEncoding().getAnnotationAsString(X_QPID_TRACE); + if (traceStr.size()) { + std::vector<std::string> trace = split(traceStr, ", "); + for (std::vector<std::string>::const_iterator i = excludes.begin(); i != excludes.end(); i++) { + for (std::vector<std::string>::const_iterator j = trace.begin(); j != trace.end(); j++) { + if (*i == *j) { + return true; + } + } + } } - //mark content loaded - loaded = true; + return false; } -// Used for testing only -void Message::tryReleaseContent() +void Message::addTraceId(const std::string& id) { - if (checkContentReleasable()) { - releaseContent(); + std::string trace = getEncoding().getAnnotationAsString(X_QPID_TRACE); + if (trace.empty()) { + annotations[X_QPID_TRACE] = id; + } else if (trace.find(id) == std::string::npos) { + trace += ","; + trace += id; + annotations[X_QPID_TRACE] = trace; } + annotationsChanged(); } -void Message::releaseContent(MessageStore* s) +void Message::clearTrace() { - //deprecated, use setStore(store); releaseContent(); instead - if (!store) setStore(s); - releaseContent(); + annotations[X_QPID_TRACE] = std::string(); + annotationsChanged(); } -void Message::releaseContent() +void Message::setTimestamp() { - sys::Mutex::ScopedLock l(lock); - if (store) { - if (!getPersistenceId()) { - intrusive_ptr<PersistableMessage> pmsg(this); - store->stage(pmsg); - staged = true; - } - //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(); - } + timestamp = ::time(0); // AMQP-0.10: posix time_t - secs since Epoch } -void Message::destroy() +uint64_t Message::getTimestamp() const { - if (staged) { - if (store) { - store->destroy(*this); - } else { - QPID_LOG(error, "Message content was staged but no store is set so it can't be destroyed"); - } - } + return timestamp; } -bool Message::getContentFrame(const Queue& queue, AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const +uint64_t Message::getTtl() const { - intrusive_ptr<const PersistableMessage> pmsg(this); - - bool done = false; - string& data = frame.castBody<AMQContentBody>()->getData(); - store->loadContent(queue, pmsg, data, offset, maxContentSize); - done = data.size() < maxContentSize; - frame.setBof(false); - frame.setEof(true); - QPID_LOG(debug, "loaded frame" << frame); - if (offset > 0) { - frame.setBos(false); - } - if (!done) { - frame.setEos(false); - } else return false; - return true; -} - -void Message::sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const -{ - sys::Mutex::ScopedLock l(lock); - if (isContentReleased() && !frames.isComplete()) { - sys::Mutex::ScopedUnlock u(lock); - uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); - bool morecontent = true; - for (uint64_t offset = 0; morecontent; offset += maxContentSize) - { - AMQFrame frame((AMQContentBody())); - morecontent = getContentFrame(queue, frame, maxContentSize, offset); - out.handle(frame); - } - queue.countLoadedFromDisk(contentSize()); + uint64_t ttl; + if (encoding->getTtl(ttl) && expiration < FAR_FUTURE) { + sys::AbsTime current( + expiryPolicy ? expiryPolicy->getCurrentTime() : sys::AbsTime::now()); + sys::Duration ttl(current, getExpiration()); + // convert from ns to ms; set to 1 if expired + return (int64_t(ttl) >= 1000000 ? int64_t(ttl)/1000000 : 1); } else { - Count c; - frames.map_if(c, TypeFilter<CONTENT_BODY>()); - - SendContent f(out, maxFrameSize, c.getCount()); - frames.map_if(f, TypeFilter<CONTENT_BODY>()); + return 0; } } -void Message::sendHeader(framing::FrameHandler& out, uint16_t /*maxFrameSize*/) const -{ - sys::Mutex::ScopedLock l(lock); - Relay f(out); - frames.map_if(f, TypeFilter<HEADER_BODY>()); - //as frame (and pointer to body) has now been passed to handler, - //subsequent modifications should use a copy - copyHeaderOnWrite = true; -} - -// TODO aconway 2007-11-09: Obsolete, remove. Was used to cover over -// 0-8/0-9 message differences. -MessageAdapter& Message::getAdapter() const +void Message::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e) { - if (!adapter) { - if(frames.isA<MessageTransferBody>()) { - adapter = &TRANSFER; - } else { - const AMQMethodBody* method = frames.getMethod(); - if (!method) throw Exception("Can't adapt message with no method"); - else throw Exception(QPID_MSG("Can't adapt message based on " << *method)); + //TODO: this is still quite 0-10 specific... + uint64_t ttl; + if (getEncoding().getTtl(ttl)) { + if (e) { + // Use higher resolution time for the internal expiry calculation. + // Prevent overflow as a signed int64_t + Duration duration(std::min(ttl * TIME_MSEC, + (uint64_t) std::numeric_limits<int64_t>::max())); + expiration = AbsTime(e->getCurrentTime(), duration); + setExpiryPolicy(e); } } - return *adapter; } -uint64_t Message::contentSize() const +void Message::addAnnotation(const std::string& key, const qpid::types::Variant& value) { - return frames.getContentSize(); + annotations[key] = value; + annotationsChanged(); } -bool Message::isContentLoaded() const +void Message::annotationsChanged() { - return loaded; + if (persistentContext) { + persistentContext = persistentContext->merge(annotations); + persistentContext->setIngressCompletion(encoding); + } } - -namespace -{ -const std::string X_QPID_TRACE("x-qpid.trace"); +void Message::setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { + expiryPolicy = e; } -bool Message::isExcluded(const std::vector<std::string>& excludes) const +bool Message::hasExpired() const { - sys::Mutex::ScopedLock l(lock); - const FieldTable* headers = getApplicationHeaders(); - if (headers) { - std::string traceStr = headers->getAsString(X_QPID_TRACE); - if (traceStr.size()) { - std::vector<std::string> trace = split(traceStr, ", "); - - for (std::vector<std::string>::const_iterator i = excludes.begin(); i != excludes.end(); i++) { - for (std::vector<std::string>::const_iterator j = trace.begin(); j != trace.end(); j++) { - if (*i == *j) { - return true; - } - } - } - } - } - return false; + return expiryPolicy && expiryPolicy->hasExpired(*this); } -class CloneHeaderBody -{ -public: - void operator()(AMQFrame& f) - { - f.cloneBody(); - } -}; - -AMQHeaderBody* Message::getHeaderBody() +uint8_t Message::getPriority() const { - // expects lock to be held - if (copyHeaderOnWrite) { - CloneHeaderBody f; - frames.map_if(f, TypeFilter<HEADER_BODY>()); - copyHeaderOnWrite = false; - } - return frames.getHeaders(); + return getEncoding().getPriority(); } -void Message::addTraceId(const std::string& id) +bool Message::getIsManagementMessage() const { return isManagementMessage; } +void Message::setIsManagementMessage(bool b) { isManagementMessage = b; } +qpid::framing::SequenceNumber Message::getSequence() const { - sys::Mutex::ScopedLock l(lock); - if (isA<MessageTransferBody>()) { - FieldTable& headers = getModifiableProperties<MessageProperties>()->getApplicationHeaders(); - std::string trace = headers.getAsString(X_QPID_TRACE); - if (trace.empty()) { - headers.setString(X_QPID_TRACE, id); - } else if (trace.find(id) == std::string::npos) { - trace += ","; - trace += id; - headers.setString(X_QPID_TRACE, trace); - } - } + return sequence; } - -void Message::clearTrace() +void Message::setSequence(const qpid::framing::SequenceNumber& s) { - sys::Mutex::ScopedLock l(lock); - if (isA<MessageTransferBody>()) { - FieldTable& headers = getModifiableProperties<MessageProperties>()->getApplicationHeaders(); - std::string trace = headers.getAsString(X_QPID_TRACE); - if (!trace.empty()) { - headers.setString(X_QPID_TRACE, ""); - } - } + sequence = s; } -void Message::setTimestamp() +MessageState Message::getState() const { - sys::Mutex::ScopedLock l(lock); - DeliveryProperties* props = getModifiableProperties<DeliveryProperties>(); - time_t now = ::time(0); - props->setTimestamp(now); // AMQP-0.10: posix time_t - secs since Epoch + return state; } - -void Message::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e) +void Message::setState(MessageState s) { - sys::Mutex::ScopedLock l(lock); - DeliveryProperties* props = getModifiableProperties<DeliveryProperties>(); - if (props->getTtl()) { - // AMQP requires setting the expiration property to be posix - // time_t in seconds. TTL is in milliseconds - if (!props->getExpiration()) { - //only set expiration in delivery properties if not already set - time_t now = ::time(0); - props->setExpiration(now + (props->getTtl()/1000)); - } - if (e) { - // Use higher resolution time for the internal expiry calculation. - // Prevent overflow as a signed int64_t - Duration ttl(std::min(props->getTtl() * TIME_MSEC, - (uint64_t) std::numeric_limits<int64_t>::max())); - expiration = AbsTime(e->getCurrentTime(), ttl); - setExpiryPolicy(e); - } - } + state = s; } -void Message::adjustTtl() +const qpid::types::Variant::Map& Message::getAnnotations() const { - sys::Mutex::ScopedLock l(lock); - DeliveryProperties* props = getModifiableProperties<DeliveryProperties>(); - if (props->getTtl()) { - if (expiration < FAR_FUTURE) { - sys::AbsTime current( - expiryPolicy ? expiryPolicy->getCurrentTime() : sys::AbsTime::now()); - sys::Duration ttl(current, getExpiration()); - // convert from ns to ms; set to 1 if expired - props->setTtl(int64_t(ttl) >= 1000000 ? int64_t(ttl)/1000000 : 1); - } - } + return annotations; } -void Message::setRedelivered() +qpid::types::Variant Message::getAnnotation(const std::string& key) const { - sys::Mutex::ScopedLock l(lock); - getModifiableProperties<framing::DeliveryProperties>()->setRedelivered(true); + qpid::types::Variant::Map::const_iterator i = annotations.find(key); + if (i != annotations.end()) return i->second; + //FIXME: modify Encoding interface to allow retrieval of + //annotations of different types from the message data as received + //off the wire + return qpid::types::Variant(getEncoding().getAnnotationAsString(key)); } -void Message::insertCustomProperty(const std::string& key, int64_t value) +std::string Message::getUserId() const { - sys::Mutex::ScopedLock l(lock); - getModifiableProperties<MessageProperties>()->getApplicationHeaders().setInt64(key,value); + return encoding->getUserId(); } -void Message::insertCustomProperty(const std::string& key, const std::string& value) +Message::Encoding& Message::getEncoding() { - sys::Mutex::ScopedLock l(lock); - getModifiableProperties<MessageProperties>()->getApplicationHeaders().setString(key,value); + return *encoding; } - -void Message::removeCustomProperty(const std::string& key) +const Message::Encoding& Message::getEncoding() const { - sys::Mutex::ScopedLock l(lock); - getModifiableProperties<MessageProperties>()->getApplicationHeaders().erase(key); + return *encoding; } - -void Message::setExchange(const std::string& exchange) +Message::operator bool() const { - sys::Mutex::ScopedLock l(lock); - getModifiableProperties<DeliveryProperties>()->setExchange(exchange); + return encoding; } -void Message::clearApplicationHeadersFlag() +std::string Message::getContent() const { - sys::Mutex::ScopedLock l(lock); - getModifiableProperties<MessageProperties>()->clearApplicationHeadersFlag(); -} - -void Message::setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { - expiryPolicy = e; + return encoding->getContent(); } -bool Message::hasExpired() +std::string Message::getPropertyAsString(const std::string& key) const { - return expiryPolicy && expiryPolicy->hasExpired(*this); + return encoding->getPropertyAsString(key); } - namespace { -struct ScopedSet { - sys::Monitor& lock; - bool& flag; - ScopedSet(sys::Monitor& l, bool& f) : lock(l), flag(f) { - sys::Monitor::ScopedLock sl(lock); - flag = true; +class PropertyRetriever : public MapHandler +{ + public: + PropertyRetriever(const std::string& key) : name(key) {} + void handleVoid(const CharSequence&) {} + void handleUint8(const CharSequence& key, uint8_t value) { handle(key, value); } + void handleUint16(const CharSequence& key, uint16_t value) { handle(key, value); } + void handleUint32(const CharSequence& key, uint32_t value) { handle(key, value); } + void handleUint64(const CharSequence& key, uint64_t value) { handle(key, value); } + void handleInt8(const CharSequence& key, int8_t value) { handle(key, value); } + void handleInt16(const CharSequence& key, int16_t value) { handle(key, value); } + void handleInt32(const CharSequence& key, int32_t value) { handle(key, value); } + void handleInt64(const CharSequence& key, int64_t value) { handle(key, value); } + void handleFloat(const CharSequence& key, float value) { handle(key, value); } + void handleDouble(const CharSequence& key, double value) { handle(key, value); } + void handleString(const CharSequence& key, const CharSequence& value, const CharSequence& /*encoding*/) + { + if (matches(key)) result = std::string(value.data, value.size); } - ~ScopedSet(){ - sys::Monitor::ScopedLock sl(lock); - flag = false; - lock.notifyAll(); + qpid::types::Variant getResult() { return result; } + + private: + std::string name; + qpid::types::Variant result; + + bool matches(const CharSequence& key) + { + return ::strncmp(key.data, name.data(), std::min(key.size, name.size())) == 0; } -}; -} -void Message::allDequeuesComplete() { - ScopedSet ss(callbackLock, inCallback); - MessageCallback* cb = dequeueCallback; - if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); + template <typename T> void handle(const CharSequence& key, T value) + { + if (matches(key)) result = value; + } +}; } - -void Message::setDequeueCompleteCallback(MessageCallback& cb) { - sys::Mutex::ScopedLock l(callbackLock); - while (inCallback) callbackLock.wait(); - dequeueCallback = &cb; +qpid::types::Variant Message::getProperty(const std::string& key) const +{ + PropertyRetriever r(key); + encoding->processProperties(r); + return r.getResult(); } -void Message::resetDequeueCompleteCallback() { - sys::Mutex::ScopedLock l(callbackLock); - while (inCallback) callbackLock.wait(); - dequeueCallback = 0; +boost::intrusive_ptr<PersistableMessage> Message::getPersistentContext() const +{ + return persistentContext; } -uint8_t Message::getPriority() const { - sys::Mutex::ScopedLock l(lock); - return getAdapter().getPriority(frames); +void Message::processProperties(MapHandler& handler) const +{ + encoding->processProperties(handler); } -bool Message::getIsManagementMessage() const { return isManagementMessage; } -void Message::setIsManagementMessage(bool b) { isManagementMessage = b; } - }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h index 90e4eec889..599819d7b6 100644 --- a/cpp/src/qpid/broker/Message.h +++ b/cpp/src/qpid/broker/Message.h @@ -23,194 +23,131 @@ */ #include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/PersistableMessage.h" -#include "qpid/broker/MessageAdapter.h" -#include "qpid/framing/amqp_types.h" -#include "qpid/sys/Monitor.h" #include "qpid/sys/Time.h" -#include <boost/function.hpp> -#include <boost/intrusive_ptr.hpp> -#include <boost/shared_ptr.hpp> -#include <memory> +#include "qpid/types/Variant.h" +//TODO: move the following out of framing or replace it +#include "qpid/framing/SequenceNumber.h" #include <string> #include <vector> -namespace qpid { - -namespace framing { -class AMQBody; -class AMQHeaderBody; -class FieldTable; -class SequenceNumber; -} +#include "qpid/RefCounted.h" +#include <boost/intrusive_ptr.hpp> +#include "qpid/broker/ExpiryPolicy.h" +#include "qpid/broker/PersistableMessage.h" +namespace qpid { namespace broker { class ConnectionToken; -class Exchange; -class ExchangeRegistry; -class MessageStore; -class Queue; -class ExpiryPolicy; +class MapHandler; + +enum MessageState +{ + AVAILABLE=1, + ACQUIRED=2, + DELETED=4, + UNAVAILABLE=8 +}; -class Message : public PersistableMessage { +class Message { public: - typedef boost::function<void (const boost::intrusive_ptr<Message>&)> MessageCallback; - - QPID_BROKER_EXTERN Message(const framing::SequenceNumber& id = framing::SequenceNumber()); + class Encoding : public AsyncCompletion + { + public: + virtual ~Encoding() {} + virtual std::string getRoutingKey() const = 0; + virtual bool isPersistent() const = 0; + virtual uint8_t getPriority() const = 0; + virtual uint64_t getContentSize() const = 0; + virtual std::string getPropertyAsString(const std::string& key) const = 0; + virtual std::string getAnnotationAsString(const std::string& key) const = 0; + virtual bool getTtl(uint64_t&) const = 0; + virtual bool hasExpiration() const = 0; + virtual std::string getContent() const = 0; + virtual void processProperties(MapHandler&) const = 0; + virtual std::string getUserId() const = 0; + }; + + QPID_BROKER_EXTERN Message(boost::intrusive_ptr<Encoding>, boost::intrusive_ptr<PersistableMessage>); + QPID_BROKER_EXTERN Message(); QPID_BROKER_EXTERN ~Message(); - uint64_t getPersistenceId() const { return persistenceId; } - void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; } - - bool getRedelivered() const { return redelivered; } - void redeliver() { redelivered = true; } + bool isRedelivered() const { return deliveryCount > 1; } + void deliver() { ++deliveryCount; } + void undeliver() { --deliveryCount; } + int getDeliveryCount() const { return deliveryCount; } + void resetDeliveryCount() { deliveryCount = 0; } const ConnectionToken* getPublisher() const { return publisher; } void setPublisher(ConnectionToken* p) { publisher = p; } - const framing::SequenceNumber& getCommandId() { return frames.getId(); } - - QPID_BROKER_EXTERN uint64_t contentSize() const; QPID_BROKER_EXTERN std::string getRoutingKey() const; - const boost::shared_ptr<Exchange> getExchange(ExchangeRegistry&) const; - QPID_BROKER_EXTERN std::string getExchangeName() const; - bool isImmediate() const; - QPID_BROKER_EXTERN const framing::FieldTable* getApplicationHeaders() const; - QPID_BROKER_EXTERN std::string getAppId() const; QPID_BROKER_EXTERN bool isPersistent() const; - bool requiresAccept(); /** determine msg expiration time using the TTL value if present */ QPID_BROKER_EXTERN void computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e); void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e); - bool hasExpired(); + + bool hasExpired() const; sys::AbsTime getExpiration() const { return expiration; } void setExpiration(sys::AbsTime exp) { expiration = exp; } - void adjustTtl(); - void setRedelivered(); - QPID_BROKER_EXTERN void insertCustomProperty(const std::string& key, int64_t value); - QPID_BROKER_EXTERN void insertCustomProperty(const std::string& key, const std::string& value); - QPID_BROKER_EXTERN void removeCustomProperty(const std::string& key); - void setExchange(const std::string&); - void clearApplicationHeadersFlag(); + uint64_t getTtl() const; + /** set the timestamp delivery property to the current time-of-day */ QPID_BROKER_EXTERN void setTimestamp(); + QPID_BROKER_EXTERN uint64_t getTimestamp() const; - framing::FrameSet& getFrames() { return frames; } - const framing::FrameSet& getFrames() const { return frames; } - - template <class T> const T* getProperties() const { - const qpid::framing::AMQHeaderBody* p = frames.getHeaders(); - return p->get<T>(); - } - - template <class T> const T* hasProperties() const { - const qpid::framing::AMQHeaderBody* p = frames.getHeaders(); - return p->get<T>(); - } - - template <class T> void eraseProperties() { - qpid::framing::AMQHeaderBody* p = frames.getHeaders(); - p->erase<T>(); - } + QPID_BROKER_EXTERN void addAnnotation(const std::string& key, const qpid::types::Variant& value); + QPID_BROKER_EXTERN bool isExcluded(const std::vector<std::string>& excludes) const; + QPID_BROKER_EXTERN void addTraceId(const std::string& id); + QPID_BROKER_EXTERN void clearTrace(); + QPID_BROKER_EXTERN uint8_t getPriority() const; + QPID_BROKER_EXTERN std::string getPropertyAsString(const std::string& key) const; + QPID_BROKER_EXTERN qpid::types::Variant getProperty(const std::string& key) const; + void processProperties(MapHandler&) const; - template <class T> const T* getMethod() const { - return frames.as<T>(); - } + QPID_BROKER_EXTERN uint64_t getContentSize() const; - template <class T> T* getMethod() { - return frames.as<T>(); - } + Encoding& getEncoding(); + const Encoding& getEncoding() const; + QPID_BROKER_EXTERN operator bool() const; - template <class T> bool isA() const { - return frames.isA<T>(); - } - - uint32_t getRequiredCredit(); - - void encode(framing::Buffer& buffer) const; - void encodeContent(framing::Buffer& buffer) const; - - /** - * @returns the size of the buffer needed to encode this - * message in its entirety - */ - uint32_t encodedSize() const; - /** - * @returns the size of the buffer needed to encode the - * 'header' of this message (not just the header frame, - * but other meta data e.g.routing key and exchange) - */ - uint32_t encodedHeaderSize() const; - uint32_t encodedContentSize() const; - - QPID_BROKER_EXTERN void decodeHeader(framing::Buffer& buffer); - QPID_BROKER_EXTERN void decodeContent(framing::Buffer& buffer); - - void QPID_BROKER_EXTERN tryReleaseContent(); - void releaseContent(); - void releaseContent(MessageStore* s);//deprecated, use 'setStore(store); releaseContent();' instead - void destroy(); - - bool getContentFrame(const Queue& queue, framing::AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const; - QPID_BROKER_EXTERN void sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const; - void sendHeader(framing::FrameHandler& out, uint16_t maxFrameSize) const; + bool getIsManagementMessage() const; + void setIsManagementMessage(bool b); - QPID_BROKER_EXTERN bool isContentLoaded() const; + QPID_BROKER_EXTERN qpid::framing::SequenceNumber getSequence() const; + QPID_BROKER_EXTERN void setSequence(const qpid::framing::SequenceNumber&); - bool isExcluded(const std::vector<std::string>& excludes) const; - void addTraceId(const std::string& id); - void clearTrace(); + MessageState getState() const; + void setState(MessageState); - void forcePersistent(); - bool isForcedPersistent(); + QPID_BROKER_EXTERN qpid::types::Variant getAnnotation(const std::string& key) const; + QPID_BROKER_EXTERN const qpid::types::Variant::Map& getAnnotations() const; + std::string getUserId() const; - /** Call cb when dequeue is complete, may call immediately. Holds cb by reference. */ - void setDequeueCompleteCallback(MessageCallback& cb); - void resetDequeueCompleteCallback(); + QPID_BROKER_EXTERN std::string getContent() const;//TODO: may be better to get rid of this... - uint8_t getPriority() const; - bool getIsManagementMessage() const; - void setIsManagementMessage(bool b); + QPID_BROKER_EXTERN boost::intrusive_ptr<AsyncCompletion> getIngressCompletion() const; + QPID_BROKER_EXTERN boost::intrusive_ptr<PersistableMessage> getPersistentContext() const; private: - MessageAdapter& getAdapter() const; - void allDequeuesComplete(); - - mutable sys::Mutex lock; - framing::FrameSet frames; - mutable boost::shared_ptr<Exchange> exchange; - mutable uint64_t persistenceId; - bool redelivered; - bool loaded; - bool staged; - bool forcePersistentPolicy; // used to force message as durable, via a broker policy + boost::intrusive_ptr<Encoding> encoding; + boost::intrusive_ptr<PersistableMessage> persistentContext; + int deliveryCount; ConnectionToken* publisher; - mutable MessageAdapter* adapter; qpid::sys::AbsTime expiration; boost::intrusive_ptr<ExpiryPolicy> expiryPolicy; - - static TransferAdapter TRANSFER; - - mutable boost::intrusive_ptr<Message> empty; - - sys::Monitor callbackLock; - MessageCallback* dequeueCallback; - bool inCallback; - - uint32_t requiredCredit; + uint64_t timestamp; + qpid::types::Variant::Map annotations; bool isManagementMessage; - mutable bool copyHeaderOnWrite; - - /** - * Expects lock to be held - */ - template <class T> T* getModifiableProperties() { - return getHeaderBody()->get<T>(true); - } - qpid::framing::AMQHeaderBody* getHeaderBody(); + MessageState state; + qpid::framing::SequenceNumber sequence; + + void annotationsChanged(); }; +QPID_BROKER_EXTERN void encode(const Message&, std::string&); +QPID_BROKER_EXTERN void decode(const std::string&, Message&); + }} diff --git a/cpp/src/qpid/broker/MessageBuilder.cpp b/cpp/src/qpid/broker/MessageBuilder.cpp index a6d605c296..7cb99514d5 100644 --- a/cpp/src/qpid/broker/MessageBuilder.cpp +++ b/cpp/src/qpid/broker/MessageBuilder.cpp @@ -21,10 +21,11 @@ #include "qpid/broker/MessageBuilder.h" #include "qpid/broker/Message.h" -#include "qpid/broker/MessageStore.h" -#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" using boost::intrusive_ptr; using namespace qpid::broker; @@ -36,8 +37,7 @@ namespace const std::string QPID_MANAGEMENT("qpid.management"); } -MessageBuilder::MessageBuilder(MessageStore* const _store) : - state(DORMANT), store(_store) {} +MessageBuilder::MessageBuilder() : state(DORMANT) {} void MessageBuilder::handle(AMQFrame& frame) { @@ -45,6 +45,7 @@ void MessageBuilder::handle(AMQFrame& frame) switch(state) { case METHOD: checkType(METHOD_BODY, type); + exchange = frame.castBody<qpid::framing::MessageTransferBody>()->getDestination(); state = HEADER; break; case HEADER: @@ -55,7 +56,9 @@ void MessageBuilder::handle(AMQFrame& frame) header.setBof(false); header.setEof(false); message->getFrames().append(header); - } else if (type != HEADER_BODY) { + } else if (type == HEADER_BODY) { + frame.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setExchange(exchange); + } else { throw CommandInvalidException( QPID_MSG("Invalid frame sequence for message, expected header or content got " << type_str(type) << ")")); @@ -73,14 +76,14 @@ void MessageBuilder::handle(AMQFrame& frame) void MessageBuilder::end() { + message->computeRequiredCredit(); message = 0; state = DORMANT; } void MessageBuilder::start(const SequenceNumber& id) { - message = intrusive_ptr<Message>(new Message(id)); - message->setStore(store); + message = intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer>(new qpid::broker::amqp_0_10::MessageTransfer(id)); state = METHOD; } @@ -112,3 +115,5 @@ void MessageBuilder::checkType(uint8_t expected, uint8_t actual) << type_str(expected) << " got " << type_str(actual) << ")")); } } + +boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> MessageBuilder::getMessage() { return message; } diff --git a/cpp/src/qpid/broker/MessageBuilder.h b/cpp/src/qpid/broker/MessageBuilder.h index b99b8efee6..5ca6fa0c66 100644 --- a/cpp/src/qpid/broker/MessageBuilder.h +++ b/cpp/src/qpid/broker/MessageBuilder.h @@ -30,21 +30,22 @@ namespace qpid { namespace broker { - class Message; - class MessageStore; + namespace amqp_0_10 { + class MessageTransfer; + } class QPID_BROKER_CLASS_EXTERN MessageBuilder : public framing::FrameHandler{ public: - QPID_BROKER_EXTERN MessageBuilder(MessageStore* const store); + QPID_BROKER_EXTERN MessageBuilder(); QPID_BROKER_EXTERN void handle(framing::AMQFrame& frame); - boost::intrusive_ptr<Message> getMessage() { return message; } + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> getMessage(); QPID_BROKER_EXTERN void start(const framing::SequenceNumber& id); void end(); private: enum State {DORMANT, METHOD, HEADER, CONTENT}; State state; - boost::intrusive_ptr<Message> message; - MessageStore* const store; + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> message; + std::string exchange; void checkType(uint8_t expected, uint8_t actual); }; diff --git a/cpp/src/qpid/broker/MessageDeque.cpp b/cpp/src/qpid/broker/MessageDeque.cpp index 83c8ca6868..1529d4ac94 100644 --- a/cpp/src/qpid/broker/MessageDeque.cpp +++ b/cpp/src/qpid/broker/MessageDeque.cpp @@ -19,218 +19,71 @@ * */ #include "qpid/broker/MessageDeque.h" -#include "qpid/broker/QueuedMessage.h" -#include "qpid/log/Statement.h" #include "assert.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/QueueCursor.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/log/Statement.h" namespace qpid { namespace broker { - -MessageDeque::MessageDeque() : available(0), head(0) {} - -size_t MessageDeque::index(const framing::SequenceNumber& position) -{ - //assuming a monotonic sequence, with no messages removed except - //from the ends of the deque, we can use the position to determin - //an index into the deque - if (messages.empty() || position < messages.front().position) return 0; - return position - messages.front().position; +namespace { +Message padding(qpid::framing::SequenceNumber id) { + Message m; + m.setState(DELETED); + m.setSequence(id); + return m; } - -bool MessageDeque::deleted(const QueuedMessage& m) -{ - size_t i = index(m.position); - if (i < messages.size()) { - QueuedMessage *qm = &messages[i]; - if (qm->status != QueuedMessage::DELETED) { - qm->status = QueuedMessage::DELETED; - qm->payload = 0; // message no longer needed - clean(); - return true; - } - } - return false; } -size_t MessageDeque::size() -{ - return available; -} +using qpid::framing::SequenceNumber; -QueuedMessage* MessageDeque::releasePtr(const QueuedMessage& message) -{ - size_t i = index(message.position); - if (i < messages.size()) { - QueuedMessage& m = messages[i]; - if (m.status == QueuedMessage::ACQUIRED) { - 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; -} +MessageDeque::MessageDeque() : messages(&padding) {} -void MessageDeque::release(const QueuedMessage& message) { releasePtr(message); } -bool MessageDeque::acquire(const framing::SequenceNumber& position, QueuedMessage& message) +bool MessageDeque::deleted(const QueueCursor& cursor) { - if (position < messages.front().position) return false; - size_t i = index(position); - if (i < messages.size()) { - QueuedMessage& temp = messages[i]; - if (temp.status == QueuedMessage::AVAILABLE) { - temp.status = QueuedMessage::ACQUIRED; - --available; - message = temp; - return true; - } - } - return false; + return messages.deleted(cursor); } -bool MessageDeque::find(const framing::SequenceNumber& position, QueuedMessage& message) +void MessageDeque::publish(const Message& added) { - size_t i = index(position); - if (i < messages.size()) { - message = messages[i]; - return true; - } else { - return false; - } + messages.publish(added); } -bool MessageDeque::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) +Message* MessageDeque::release(const QueueCursor& cursor) { - //get first message that is greater than position - size_t i = index(position + 1); - while (i < messages.size()) { - QueuedMessage& m = messages[i++]; - if (m.status == QueuedMessage::AVAILABLE || (!unacquired && m.status == QueuedMessage::ACQUIRED)) { - message = m; - return true; - } - } - return false; + return messages.release(cursor); } -bool MessageDeque::consume(QueuedMessage& message) +Message* MessageDeque::next(QueueCursor& cursor) { - while (head < messages.size()) { - QueuedMessage& i = messages[head++]; - if (i.status == QueuedMessage::AVAILABLE) { - i.status = QueuedMessage::ACQUIRED; - --available; - message = i; - return true; - } - } - return false; + return messages.next(cursor); } -namespace { -QueuedMessage padding(uint32_t pos) { - return QueuedMessage(0, 0, pos, QueuedMessage::DELETED); -} -} // namespace - -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) - messages.push_back(padding(messages.back().position + 1)); - messages.push_back(added); - messages.back().status = QueuedMessage::AVAILABLE; - if (head >= messages.size()) head = messages.size() - 1; - ++available; - clean(); // QPID-4046: let producer help clean the backlog of deleted messages - 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) +size_t MessageDeque::size() { - // Pad the front of the queue if necessary - while (messages.size() && (acquired.position < messages.front().position)) - messages.push_front(padding(uint32_t(messages.front().position) - 1)); - size_t i = index(acquired.position); - if (i < messages.size()) { // Replace an existing padding message - assert(messages[i].status == QueuedMessage::DELETED); - messages[i] = acquired; - messages[i].status = QueuedMessage::ACQUIRED; - } - else { // Push to the back - // Pad the back of the queue if necessary - while (messages.size() && (acquired.position - messages.back().position) > 1) - messages.push_back(padding(messages.back().position + 1)); - assert(!messages.size() || (acquired.position - messages.back().position) == 1); - messages.push_back(acquired); - messages.back().status = QueuedMessage::ACQUIRED; - } + return messages.size(); } -namespace { -bool isNotDeleted(const QueuedMessage& qm) { return qm.status != QueuedMessage::DELETED; } -} // namespace - -void MessageDeque::setPosition(const framing::SequenceNumber& n) { - size_t i = index(n+1); - if (i >= messages.size()) return; // Nothing to do. - - // Assertion to verify the precondition: no messaages after n. - assert(std::find_if(messages.begin()+i, messages.end(), &isNotDeleted) == - messages.end()); - messages.erase(messages.begin()+i, messages.end()); - if (head >= messages.size()) head = messages.size() - 1; - // Re-count the available messages - available = 0; - for (Deque::iterator i = messages.begin(); i != messages.end(); ++i) { - if (i->status == QueuedMessage::AVAILABLE) ++available; - } +Message* MessageDeque::find(const framing::SequenceNumber& position, QueueCursor* cursor) +{ + return messages.find(position, cursor); } -void MessageDeque::clean() +Message* MessageDeque::find(const QueueCursor& cursor) { - // QPID-4046: If a queue has multiple consumers, then it is possible for a large - // collection of deleted messages to build up. Limit the number of messages cleaned - // up on each call to clean(). - size_t count = 0; - while (messages.size() && messages.front().status == QueuedMessage::DELETED && count < 10) { - messages.pop_front(); - count += 1; - } - head = (head > count) ? head - count : 0; + return messages.find(cursor); } void MessageDeque::foreach(Functor f) { - for (Deque::iterator i = messages.begin(); i != messages.end(); ++i) { - if (i->status == QueuedMessage::AVAILABLE) { - f(*i); - } - } + messages.foreach(f); } -void MessageDeque::removeIf(Predicate p) +void MessageDeque::resetCursors() { - for (Deque::iterator i = messages.begin(); i != messages.end(); ++i) { - if (i->status == QueuedMessage::AVAILABLE && p(*i)) { - //Use special status for this as messages are not yet - //dequeued, but should not be considered on the queue - //either (used for purging and moving) - i->status = QueuedMessage::REMOVED; - --available; - } - } - clean(); + messages.resetCursors(); } }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/MessageDeque.h b/cpp/src/qpid/broker/MessageDeque.h index c5670b2a72..ec67476926 100644 --- a/cpp/src/qpid/broker/MessageDeque.h +++ b/cpp/src/qpid/broker/MessageDeque.h @@ -22,8 +22,7 @@ * */ #include "qpid/broker/Messages.h" -#include "qpid/broker/QueuedMessage.h" -#include <deque> +#include "qpid/broker/IndexedDeque.h" namespace qpid { namespace broker { @@ -36,31 +35,20 @@ class MessageDeque : public Messages public: MessageDeque(); size_t size(); - bool deleted(const QueuedMessage&); - void release(const QueuedMessage&); - bool acquire(const framing::SequenceNumber&, QueuedMessage&); - bool find(const framing::SequenceNumber&, QueuedMessage&); - bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); - bool consume(QueuedMessage&); - bool push(const QueuedMessage& added, QueuedMessage& removed); - void updateAcquired(const QueuedMessage& acquired); - void setPosition(const framing::SequenceNumber&); + bool deleted(const QueueCursor&); + void publish(const Message& added); + Message* next(QueueCursor&); + Message* release(const QueueCursor& cursor); + Message* find(const QueueCursor&); + Message* find(const framing::SequenceNumber&, QueueCursor*); + 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); + void resetCursors(); private: - typedef std::deque<QueuedMessage> Deque; + typedef IndexedDeque<Message> Deque; Deque messages; - size_t available; - size_t head; - - size_t index(const framing::SequenceNumber&); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/MessageDistributor.h b/cpp/src/qpid/broker/MessageDistributor.h index 090393c160..c11e4495a1 100644 --- a/cpp/src/qpid/broker/MessageDistributor.h +++ b/cpp/src/qpid/broker/MessageDistributor.h @@ -21,51 +21,28 @@ * under the License. * */ - +#include "qpid/types/Variant.h" /** Abstraction used by Queue to determine the next "most desirable" message to provide to * a particular consuming client */ - -#include "qpid/broker/Consumer.h" - namespace qpid { namespace broker { -struct QueuedMessage; +class Message; class MessageDistributor { public: virtual ~MessageDistributor() {}; - /** Locking Note: all methods assume the caller is holding the Queue::messageLock - * during the method call. - */ - - /** Determine the next message available for consumption by the consumer - * @param consumer the consumer that needs a message to consume - * @param next set to the next message that the consumer may consume. - * @return true if message is available and next is set - */ - virtual bool nextConsumableMessage( Consumer::shared_ptr& consumer, - QueuedMessage& next ) = 0; - - /** Allow the comsumer to take ownership of the given message. + /** + * Determine whether the named consumer can take ownership of the specified message. * @param consumer the name of the consumer that is attempting to acquire the message - * @param qm the message to be acquired, previously returned from nextConsumableMessage() + * @param target the message to be acquired * @return true if ownership is permitted, false if ownership cannot be assigned. */ - virtual bool allocate( const std::string& consumer, - const QueuedMessage& target) = 0; - - /** Determine the next message available for browsing by the consumer - * @param consumer the consumer that is browsing the queue - * @param next set to the next message that the consumer may browse. - * @return true if a message is available and next is returned - */ - virtual bool nextBrowsableMessage( Consumer::shared_ptr& consumer, - QueuedMessage& next ) = 0; + virtual bool acquire(const std::string& consumer, Message& target) = 0; /** hook to add any interesting management state to the status map */ virtual void query(qpid::types::Variant::Map&) const = 0; diff --git a/cpp/src/qpid/broker/MessageGroupManager.cpp b/cpp/src/qpid/broker/MessageGroupManager.cpp index 15cd56a676..47e40a4794 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.cpp +++ b/cpp/src/qpid/broker/MessageGroupManager.cpp @@ -1,4 +1,4 @@ -/* + /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -20,10 +20,16 @@ */ #include "qpid/broker/MessageGroupManager.h" - -#include "qpid/broker/Queue.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Messages.h" +#include "qpid/broker/MessageDeque.h" +#include "qpid/broker/QueueSettings.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/DeliveryProperties.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" +#include "qpid/framing/TypeCode.h" +#include "qpid/types/Variant.h" #include "qpid/log/Statement.h" #include "qpid/types/Variant.h" @@ -75,24 +81,16 @@ void MessageGroupManager::disown( GroupState& state ) freeGroups[state.members.front().position] = &state; } -MessageGroupManager::GroupState& MessageGroupManager::findGroup( const QueuedMessage& qm ) +MessageGroupManager::GroupState& MessageGroupManager::findGroup( const Message& m ) { - uint32_t thisMsg = qm.position.getValue(); + uint32_t thisMsg = m.getSequence().getValue(); if (cachedGroup && lastMsg == thisMsg) { hits++; return *cachedGroup; } - std::string group = defaultGroupId; - const qpid::framing::FieldTable* headers = qm.payload->getApplicationHeaders(); - if (headers) { - qpid::framing::FieldTable::ValuePtr id = headers->get( groupIdHeader ); - if (id && id->convertsTo<std::string>()) { - std::string tmp = id->get<std::string>(); - if (!tmp.empty()) // empty group is reserved - group = tmp; - } - } + std::string group = m.getPropertyAsString(groupIdHeader); + if (group.empty()) group = defaultGroupId; //empty group is reserved if (cachedGroup && group == lastGroup) { hits++; @@ -112,48 +110,48 @@ MessageGroupManager::GroupState& MessageGroupManager::findGroup( const QueuedMes } -void MessageGroupManager::enqueued( const QueuedMessage& qm ) +void MessageGroupManager::enqueued( const Message& m ) { // @todo KAG optimization - store reference to group state in QueuedMessage // issue: const-ness?? - GroupState& state = findGroup(qm); - GroupState::MessageState mState(qm.position); + GroupState& state = findGroup(m); + GroupState::MessageState mState(m.getSequence()); 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 ); if (total == 1) { // newly created group, no owner - assert(freeGroups.find(qm.position) == freeGroups.end()); - freeGroups[qm.position] = &state; + assert(freeGroups.find(m.getSequence()) == freeGroups.end()); + freeGroups[m.getSequence()] = &state; } } -void MessageGroupManager::acquired( const QueuedMessage& qm ) +void MessageGroupManager::acquired( const Message& m ) { // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? - GroupState& state = findGroup(qm); - GroupState::MessageFifo::iterator m = state.findMsg(qm.position); - assert(m != state.members.end()); - m->acquired = true; + GroupState& state = findGroup(m); + GroupState::MessageFifo::iterator gm = state.findMsg(m.getSequence()); + assert(gm != state.members.end()); + gm->acquired = true; state.acquired += 1; QPID_LOG( trace, "group queue " << qName << ": acquired message in group id=" << state.group << " acquired=" << state.acquired ); } -void MessageGroupManager::requeued( const QueuedMessage& qm ) +void MessageGroupManager::requeued( const Message& m ) { // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? - GroupState& state = findGroup(qm); + GroupState& state = findGroup(m); assert( state.acquired != 0 ); state.acquired -= 1; - GroupState::MessageFifo::iterator m = state.findMsg(qm.position); - assert(m != state.members.end()); - m->acquired = false; + GroupState::MessageFifo::iterator i = state.findMsg(m.getSequence()); + assert(i != state.members.end()); + i->acquired = false; if (state.acquired == 0 && state.owned()) { QPID_LOG( trace, "group queue " << qName << ": consumer name=" << state.owner << " released group id=" << state.group); @@ -164,14 +162,14 @@ void MessageGroupManager::requeued( const QueuedMessage& qm ) } -void MessageGroupManager::dequeued( const QueuedMessage& qm ) +void MessageGroupManager::dequeued( const Message& m ) { // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? - GroupState& state = findGroup(qm); - GroupState::MessageFifo::iterator m = state.findMsg(qm.position); - assert(m != state.members.end()); - if (m->acquired) { + GroupState& state = findGroup(m); + GroupState::MessageFifo::iterator i = state.findMsg(m.getSequence()); + assert(i != state.members.end()); + if (i->acquired) { assert( state.acquired != 0 ); state.acquired -= 1; } @@ -179,7 +177,7 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) // 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 (m == state.members.begin()) { + if (i == 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! @@ -188,7 +186,7 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) } state.members.pop_front(); } else { - state.members.erase(m); + state.members.erase(i); } uint32_t total = state.members.size(); @@ -206,6 +204,12 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) QPID_LOG( trace, "group queue " << qName << ": consumer name=" << state.owner << " released group id=" << state.group); disown(state); + MessageDeque* md = dynamic_cast<MessageDeque*>(&messages); + if (md) { + md->resetCursors(); + } else { + QPID_LOG(warning, "Could not reset cursors for message group, unexpected container type"); + } } else if (reFreeNeeded) { disown(state); } @@ -215,55 +219,27 @@ MessageGroupManager::~MessageGroupManager() { QPID_LOG( debug, "group queue " << qName << " cache results: hits=" << hits << " misses=" << misses ); } -bool MessageGroupManager::nextConsumableMessage( Consumer::shared_ptr& c, QueuedMessage& next ) + +bool MessageGroupManager::acquire(const std::string& consumer, Message& m) { - if (!messages.size()) - return false; + if (m.getState() == AVAILABLE) { + // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage + GroupState& state = findGroup(m); - next.position = c->getPosition(); - if (!freeGroups.empty()) { - const framing::SequenceNumber& nextFree = freeGroups.begin()->first; - if (nextFree <= next.position) { // take oldest free - next.position = nextFree; - --next.position; + if (!state.owned()) { + own( state, consumer ); + QPID_LOG( trace, "group queue " << qName << + ": consumer name=" << consumer << " has acquired group id=" << state.group); } - } - - while (messages.browse( next.position, next, true )) { - GroupState& group = findGroup(next); - if (!group.owned()) { - //TODO: make acquire more efficient when we already have the message in question - 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().position); - } else if (group.owner == c->getName() && messages.acquire(next.position, next)) { + if (state.owner == consumer) { + m.setState(ACQUIRED); return true; + } else { + return false; } + } else { + return false; } - return false; -} - - -bool MessageGroupManager::allocate(const std::string& consumer, const QueuedMessage& qm) -{ - // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage - GroupState& state = findGroup(qm); - - if (!state.owned()) { - own( state, consumer ); - QPID_LOG( trace, "group queue " << qName << - ": consumer name=" << consumer << " has acquired group id=" << state.group); - return true; - } - return state.owner == consumer; -} - -bool MessageGroupManager::nextBrowsableMessage( Consumer::shared_ptr& c, QueuedMessage& next ) -{ - // browse: allow access to any available msg, regardless of group ownership (?ok?) - return messages.browse(c->getPosition(), next, false); } void MessageGroupManager::query(qpid::types::Variant::Map& status) const @@ -296,11 +272,9 @@ void MessageGroupManager::query(qpid::types::Variant::Map& status) const // set the timestamp to the arrival timestamp of the oldest (HEAD) message, if present info[GROUP_TIMESTAMP] = 0; if (g->second.members.size() != 0) { - QueuedMessage 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(); + Message* m = messages.find(g->second.members.front().position, 0); + if (m && m->getTimestamp()) { + info[GROUP_TIMESTAMP] = m->getTimestamp(); } } info[GROUP_CONSUMER] = g->second.owner; @@ -313,33 +287,13 @@ void MessageGroupManager::query(qpid::types::Variant::Map& status) const boost::shared_ptr<MessageGroupManager> MessageGroupManager::create( const std::string& qName, Messages& messages, - const qpid::framing::FieldTable& settings ) + const QueueSettings& settings ) { - boost::shared_ptr<MessageGroupManager> empty; - - if (settings.isSet(qpidMessageGroupKey)) { - - // @todo: remove once "sticky" consumers are supported - see QPID-3347 - if (!settings.isSet(qpidSharedGroup)) { - QPID_LOG( error, "Only shared groups are supported in this version of the broker. Use '--shared-groups' in qpid-config." ); - return empty; - } - - std::string headerKey = settings.getAsString(qpidMessageGroupKey); - if (headerKey.empty()) { - QPID_LOG( error, "A Message Group header key must be configured, queue=" << qName); - return empty; - } - unsigned int timestamp = settings.getAsInt(qpidMessageGroupTimestamp); - - boost::shared_ptr<MessageGroupManager> manager( new MessageGroupManager( headerKey, qName, messages, timestamp ) ); - - QPID_LOG( debug, "Configured Queue '" << qName << - "' for message grouping using header key '" << headerKey << "'" << - " (timestamp=" << timestamp << ")"); - return manager; - } - return empty; + boost::shared_ptr<MessageGroupManager> manager( new MessageGroupManager( settings.groupKey, qName, messages, settings.addTimestamp ) ); + QPID_LOG( debug, "Configured Queue '" << qName << + "' for message grouping using header key '" << settings.groupKey << "'" << + " (timestamp=" << settings.addTimestamp << ")"); + return manager; } std::string MessageGroupManager::defaultGroupId; diff --git a/cpp/src/qpid/broker/MessageGroupManager.h b/cpp/src/qpid/broker/MessageGroupManager.h index 2dd97ea2ff..fe39e007b5 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.h +++ b/cpp/src/qpid/broker/MessageGroupManager.h @@ -24,8 +24,10 @@ /* for managing message grouping on Queues */ +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/StatefulQueueObserver.h" #include "qpid/broker/MessageDistributor.h" +#include "qpid/framing/SequenceNumber.h" #include "qpid/sys/unordered_map.h" #include <deque> @@ -34,6 +36,7 @@ namespace qpid { namespace broker { class QueueObserver; +struct QueueSettings; class MessageDistributor; class Messages; @@ -76,11 +79,7 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu GroupFifo freeGroups; // ordered by oldest free msg //Consumers consumers; // index: consumer name - static const std::string qpidMessageGroupKey; - static const std::string qpidSharedGroup; // if specified, one group can be consumed by multiple receivers - static const std::string qpidMessageGroupTimestamp; - - GroupState& findGroup( const QueuedMessage& qm ); + GroupState& findGroup( const Message& m ); unsigned long hits, misses; // for debug uint32_t lastMsg; std::string lastGroup; @@ -91,11 +90,14 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu void disown( GroupState& state ); public: + static const std::string qpidMessageGroupKey; + static const std::string qpidSharedGroup; // if specified, one group can be consumed by multiple receivers + static const std::string qpidMessageGroupTimestamp; static QPID_BROKER_EXTERN void setDefaults(const std::string& groupId); static boost::shared_ptr<MessageGroupManager> create( const std::string& qName, Messages& messages, - const qpid::framing::FieldTable& settings ); + const QueueSettings& settings ); MessageGroupManager(const std::string& header, const std::string& _qName, Messages& container, unsigned int _timestamp=0 ) @@ -106,22 +108,20 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu virtual ~MessageGroupManager(); // QueueObserver iface - void enqueued( const QueuedMessage& qm ); - void acquired( const QueuedMessage& qm ); - void requeued( const QueuedMessage& qm ); - void dequeued( const QueuedMessage& qm ); + void enqueued( const Message& qm ); + void acquired( const Message& qm ); + void requeued( const Message& qm ); + void dequeued( const Message& qm ); void consumerAdded( const Consumer& ) {}; void consumerRemoved( const Consumer& ) {}; void getState(qpid::framing::FieldTable& state ) const; void setState(const qpid::framing::FieldTable&); // MessageDistributor iface - bool nextConsumableMessage(Consumer::shared_ptr& c, QueuedMessage& next); - bool allocate(const std::string& c, const QueuedMessage& qm); - bool nextBrowsableMessage(Consumer::shared_ptr& c, QueuedMessage& next); + bool acquire(const std::string& c, Message& ); void query(qpid::types::Variant::Map&) const; - bool match(const qpid::types::Variant::Map*, const QueuedMessage&) const; + bool match(const qpid::types::Variant::Map*, const Message&) const; }; }} diff --git a/cpp/src/qpid/broker/MessageMap.cpp b/cpp/src/qpid/broker/MessageMap.cpp index 592f3fefde..4cdd83c9aa 100644 --- a/cpp/src/qpid/broker/MessageMap.cpp +++ b/cpp/src/qpid/broker/MessageMap.cpp @@ -19,7 +19,8 @@ * */ #include "qpid/broker/MessageMap.h" -#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/QueueCursor.h" #include "qpid/log/Statement.h" #include <algorithm> @@ -29,29 +30,17 @@ namespace { const std::string EMPTY; } -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) +std::string MessageMap::getKey(const Message& message) { - const framing::FieldTable* ft = message.payload->getApplicationHeaders(); - if (ft) return ft->getAsString(key); - else return EMPTY; + return message.getPropertyAsString(key); } size_t MessageMap::size() { size_t count(0); for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { - if (i->second.status == QueuedMessage::AVAILABLE) ++count; + if (i->second.getState() == AVAILABLE) ++count; } return count; } @@ -61,116 +50,103 @@ bool MessageMap::empty() return size() == 0;//TODO: more efficient implementation } -void MessageMap::release(const QueuedMessage& message) +bool MessageMap::deleted(const QueueCursor& cursor) { - 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() && i->second.status == QueuedMessage::AVAILABLE) { - i->second.status = QueuedMessage::ACQUIRED; - message = i->second; + Ordering::iterator i = messages.find(cursor.position); + if (i != messages.end()) { + erase(i); return true; } else { return false; } } -bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& message) +Message* MessageMap::find(const QueueCursor& cursor) { - Ordering::iterator i = messages.find(position); - if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) { - message = i->second; - return true; - } else { - return false; - } + if (cursor.valid) return find(cursor.position, 0); + else return 0; } -bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) +Message* MessageMap::find(const framing::SequenceNumber& position, QueueCursor* cursor) { - Ordering::iterator i = messages.lower_bound(position+1); - if (i != messages.end() && (i->second.status == QueuedMessage::AVAILABLE || (!unacquired && i->second.status == QueuedMessage::ACQUIRED))) { - message = i->second; - return true; + Ordering::iterator i = messages.lower_bound(position); + if (i != messages.end()) { + if (cursor) cursor->setPosition(i->first, version); + if (i->first == position) return &(i->second); + else return 0; } else { - return false; + //there is no message whose sequence is greater than position, + //i.e. haven't got there yet + if (cursor) cursor->setPosition(position, version); + return 0; } } -bool MessageMap::consume(QueuedMessage& message) +Message* MessageMap::next(QueueCursor& cursor) { - 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; + Ordering::iterator i; + if (!cursor.valid) i = messages.begin(); //start with oldest message + else i = messages.upper_bound(cursor.position); //get first message that is greater than position + + while (i != messages.end()) { + Message& m = i->second; + cursor.setPosition(m.getSequence(), version); + if (cursor.check(m)) { + return &m; + } else { + ++i; } } - return false; + return 0; } -const QueuedMessage& MessageMap::replace(const QueuedMessage& original, const QueuedMessage& update) +const Message& MessageMap::replace(const Message& original, const Message& update) { - messages.erase(original.position); - messages[update.position] = update; - return update; + messages.erase(original.getSequence()); + std::pair<Ordering::iterator, bool> i = messages.insert(Ordering::value_type(update.getSequence(), update)); + i.first->second.setState(AVAILABLE); + return i.first->second; } -bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed) +void MessageMap::publish(const Message& added) +{ + Message dummy; + update(added, dummy); +} + +bool MessageMap::update(const Message& added, Message& removed) { std::pair<Index::iterator, bool> result = index.insert(Index::value_type(getKey(added), added)); if (result.second) { //there was no previous message for this key; nothing needs to //be removed, just add the message into its correct position - QueuedMessage& a = messages[added.position]; - a = added; - a.status = QueuedMessage::AVAILABLE; - QPID_LOG(debug, "Added message " << a); + messages.insert(Ordering::value_type(added.getSequence(), added)).first->second.setState(AVAILABLE); 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 " << removed << " with " << result.first->second << ": " << result.first->first); + result.first->second.setState(AVAILABLE); + QPID_LOG(debug, "Displaced message at " << removed.getSequence() << " with " << result.first->second.getSequence() << ": " << result.first->first); return true; } } -void MessageMap::setPosition(const framing::SequenceNumber& seq) { - // Nothing to do, just assert that the precondition is respected and there - // are no undeleted messages after seq. - (void) seq; assert(messages.empty() || (--messages.end())->first <= seq); -} - -void MessageMap::foreach(Functor f) +Message* MessageMap::release(const QueueCursor& cursor) { - for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { - if (i->second.status == QueuedMessage::AVAILABLE) f(i->second); + Ordering::iterator i = messages.find(cursor.position); + if (i != messages.end()) { + i->second.setState(AVAILABLE); + return &i->second; + } else { + return 0; } } -void MessageMap::removeIf(Predicate p) +void MessageMap::foreach(Functor f) { - 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; - } + for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { + if (i->second.getState() == AVAILABLE) f(i->second); } } @@ -180,6 +156,6 @@ void MessageMap::erase(Ordering::iterator i) messages.erase(i); } -MessageMap::MessageMap(const std::string& k) : key(k) {} +MessageMap::MessageMap(const std::string& k) : key(k), version(0) {} }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/MessageMap.h b/cpp/src/qpid/broker/MessageMap.h index 1f0481cb6b..c30606d0ff 100644 --- a/cpp/src/qpid/broker/MessageMap.h +++ b/cpp/src/qpid/broker/MessageMap.h @@ -6,7 +6,7 @@ * 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 -o * regarding copyright ownership. The ASF licenses this file + * 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 @@ -22,6 +22,7 @@ o * regarding copyright ownership. The ASF licenses this file * */ #include "qpid/broker/Messages.h" +#include "qpid/broker/Message.h" #include "qpid/framing/SequenceNumber.h" #include <map> #include <string> @@ -38,32 +39,31 @@ class MessageMap : public Messages { public: MessageMap(const std::string& key); - virtual ~MessageMap() {} size_t size(); bool empty(); - virtual bool deleted(const QueuedMessage&); - void release(const QueuedMessage&); - virtual bool acquire(const framing::SequenceNumber&, QueuedMessage&); - bool find(const framing::SequenceNumber&, QueuedMessage&); - virtual bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); - bool consume(QueuedMessage&); - virtual bool push(const QueuedMessage& added, QueuedMessage& removed); - void setPosition(const framing::SequenceNumber&); + bool deleted(const QueueCursor&); + void publish(const Message& added);//use update instead to get replaced message + Message* next(QueueCursor&); + Message* release(const QueueCursor& cursor); + Message* find(const QueueCursor&); + Message* find(const framing::SequenceNumber&, QueueCursor*); void foreach(Functor); - virtual void removeIf(Predicate); + + bool update(const Message& added, Message& removed); protected: - typedef std::map<std::string, QueuedMessage> Index; - typedef std::map<framing::SequenceNumber, QueuedMessage> Ordering; + typedef std::map<std::string, Message> Index; + typedef std::map<framing::SequenceNumber, Message> Ordering; const std::string key; Index index; Ordering messages; + int32_t version; - std::string getKey(const QueuedMessage&); - virtual const QueuedMessage& replace(const QueuedMessage&, const QueuedMessage&); + std::string getKey(const Message&); + virtual const Message& replace(const Message&, const Message&); void erase(Ordering::iterator); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Messages.h b/cpp/src/qpid/broker/Messages.h index 45f5e6cd81..a94ac7e0bf 100644 --- a/cpp/src/qpid/broker/Messages.h +++ b/cpp/src/qpid/broker/Messages.h @@ -29,7 +29,8 @@ namespace framing { class SequenceNumber; } namespace broker { -struct QueuedMessage; +class Message; +class QueueCursor; /** * This interface abstracts out the access to the messages held for @@ -39,8 +40,7 @@ struct QueuedMessage; class Messages { public: - typedef boost::function1<void, QueuedMessage&> Functor; - typedef boost::function1<bool, QueuedMessage&> Predicate; + typedef boost::function1<void, Message&> Functor; virtual ~Messages() {} /** @@ -51,47 +51,44 @@ class Messages /** * Called when a message is deleted from the queue. */ - virtual bool deleted(const QueuedMessage&) = 0; + virtual bool deleted(const QueueCursor&) = 0; /** - * Releases an acquired message, making it available again. + * Makes a message available. */ - virtual void release(const QueuedMessage&) = 0; + virtual void publish(const Message& added) = 0; /** - * Acquire the message at the specified position, returning true - * if found, false otherwise. The acquired message is passed back - * via the second parameter. - */ - virtual bool acquire(const framing::SequenceNumber&, QueuedMessage&) = 0; - /** - * Find the message at the specified position, returning true if - * found, false otherwise. The matched message is passed back via - * the second parameter. + * Retrieve the next message for the given cursor. A reference to + * the message is passed back via the second parameter. + * + * @return a pointer to the message if there is one, in which case + * the cursor that points to it is assigned to cursor; null + * otherwise. */ - virtual bool find(const framing::SequenceNumber&, QueuedMessage&) = 0; + virtual Message* next(QueueCursor& cursor) = 0; + /** - * Retrieve the next message to be given to a browsing - * subscription that has reached the specified position. The next - * message is passed back via the second parameter. + * Release the message i.e. return it to the available state + * unless it has already been deleted. * - * @param unacquired, if true, will only browse unacquired messages - * - * @return true if there is another message, false otherwise. + * @return a pointer to the Message if it is still in acquired state and + * hence can be released; null if it has already been deleted */ - virtual bool browse(const framing::SequenceNumber&, QueuedMessage&, bool unacquired) = 0; + virtual Message* release(const QueueCursor& cursor) = 0; /** - * Retrieve the next message available for a consuming - * subscription. - * - * @return true if there is such a message, false otherwise. + * Find the message with the specified sequence number, returning + * a pointer if found, null otherwise. A cursor to the matched + * message can be passed back via the second parameter, regardless + * of whether the message is found, using this cursor to call + * next() will give the next message greater than position if one + * exists. */ - virtual bool consume(QueuedMessage&) = 0; + virtual Message* find(const framing::SequenceNumber&, QueueCursor*) = 0; + /** - * Pushes a message to the back of the 'queue'. For some types of - * queue this may cause another message to be removed; if that is - * the case the method will return true and the removed message - * will be passed out via the second parameter. + * Find the message at the specified position, returning a pointer if + * found, null otherwise. */ - virtual bool push(const QueuedMessage& added, QueuedMessage& removed) = 0; + virtual Message* find(const QueueCursor&) = 0; /** * Add an already acquired message to the queue. @@ -99,25 +96,11 @@ class Messages * Only need be implemented by subclasses that keep track of * acquired messages. */ - virtual void updateAcquired(const QueuedMessage&) { } - - /** - * Set the position of the back of the queue. Next message enqueued will be n+1. - *@pre Any messages with seq > n must already be dequeued. - */ - virtual void setPosition(const framing::SequenceNumber& /*n*/) = 0; - + //virtual void updateAcquired(const QueuedMessage&) { } /** * Apply, the functor to each message held */ - virtual void foreach(Functor) = 0; - /** - * Remove every message held that for which the specified - * predicate returns true - */ - virtual void removeIf(Predicate) = 0; - private: }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Persistable.h b/cpp/src/qpid/broker/Persistable.h index 36499c7a1a..444aca3295 100644 --- a/cpp/src/qpid/broker/Persistable.h +++ b/cpp/src/qpid/broker/Persistable.h @@ -32,7 +32,7 @@ namespace broker { /** * Base class for all persistable objects */ -class Persistable : public RefCounted +class Persistable : public virtual RefCounted { public: /** diff --git a/cpp/src/qpid/broker/PersistableMessage.cpp b/cpp/src/qpid/broker/PersistableMessage.cpp index 957248b522..9601b8dcce 100644 --- a/cpp/src/qpid/broker/PersistableMessage.cpp +++ b/cpp/src/qpid/broker/PersistableMessage.cpp @@ -30,163 +30,58 @@ using namespace qpid::broker; namespace qpid { namespace broker { +PersistableMessage::PersistableMessage() : ingressCompletion(0), persistenceId(0) {} PersistableMessage::~PersistableMessage() {} -PersistableMessage::PersistableMessage() : - asyncDequeueCounter(0), - store(0), - asyncStore(0) -{} - -void PersistableMessage::flush() +void PersistableMessage::setIngressCompletion(boost::intrusive_ptr<AsyncCompletion> i) { - syncList copy; - { - sys::ScopedLock<sys::Mutex> l(storeLock); - if (store) { - copy = synclist; - } else { - return;//early exit as nothing to do - } + ingressCompletion = i.get(); + /** + * What follows is a hack to account for the fact that the + * AsyncCompletion to use may be, but is not always, this same + * object. + * + * This is hopefully temporary, and allows the store interface to + * remain unchanged without requiring another object to be allocated + * for every message. + * + * The case in question is where a message previously passed to + * the store is modified by some other queue onto which it is + * pushed, and then again persisted to the store. These will be + * two separate PersistableMessage instances (since the latter now + * has different content), but need to share the same + * AsyncCompletion (since they refer to the same incoming transfer + * command). + */ + if (static_cast<RefCounted*>(ingressCompletion) != static_cast<RefCounted*>(this)) { + holder = i; } - for (syncList::iterator i = copy.begin(); i != copy.end(); ++i) { - PersistableQueue::shared_ptr q(i->lock()); - if (q) { - q->flush(); - } - } -} - -void PersistableMessage::setContentReleased() -{ - contentReleaseState.released = true; } -bool PersistableMessage::isContentReleased() const -{ - return contentReleaseState.released; -} - - -bool PersistableMessage::isStoredOnQueue(PersistableQueue::shared_ptr queue){ - if (store && (queue->getPersistenceId()!=0)) { - for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) { - PersistableQueue::shared_ptr q(i->lock()); - if (q && q->getPersistenceId() == queue->getPersistenceId()) return true; - } - } - return false; -} - -// deprecated -void PersistableMessage::addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store) { - if (_store){ - sys::ScopedLock<sys::Mutex> l(storeLock); - store = _store; - boost::weak_ptr<PersistableQueue> q(queue); - synclist.push_back(q); - } -} -void PersistableMessage::addToSyncList(PersistableQueue::shared_ptr queue, AsyncStore* _store) { - if (_store){ - sys::ScopedLock<sys::Mutex> l(storeLock); - asyncStore = _store; - boost::weak_ptr<PersistableQueue> q(queue); - synclist.push_back(q); - } +void PersistableMessage::flush() +{ + //TODO: is this really the right place for this? } // deprecated -void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { - addToSyncList(queue, _store); +void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr, MessageStore*) +{ enqueueStart(); } -void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr queue, AsyncStore* _store) { - addToSyncList(queue, _store); +void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr, AsyncStore*) +{ enqueueStart(); } -bool PersistableMessage::isDequeueComplete() { - sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); - return asyncDequeueCounter == 0; -} - -void PersistableMessage::dequeueComplete() { - bool notify = false; - { - sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); - if (asyncDequeueCounter > 0) { - if (--asyncDequeueCounter == 0) { - notify = true; - } - } - } - if (notify) allDequeuesComplete(); -} - -// deprecated -void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { - if (_store){ - sys::ScopedLock<sys::Mutex> l(storeLock); - store = _store; - boost::weak_ptr<PersistableQueue> q(queue); - synclist.push_back(q); - } - dequeueAsync(); -} - -void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr queue, AsyncStore* _store) { - if (_store){ - sys::ScopedLock<sys::Mutex> l(storeLock); - asyncStore = _store; - boost::weak_ptr<PersistableQueue> q(queue); - synclist.push_back(q); - } - dequeueAsync(); -} - -void PersistableMessage::dequeueAsync() { - sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); - asyncDequeueCounter++; -} - -PersistableMessage::ContentReleaseState::ContentReleaseState() : blocked(false), requested(false), released(false) {} - // deprecated -void PersistableMessage::setStore(MessageStore* s) -{ - store = s; -} - -void PersistableMessage::setStore(AsyncStore* s) -{ - asyncStore = s; -} +void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr, MessageStore*) {} -void PersistableMessage::requestContentRelease() -{ - contentReleaseState.requested = true; -} -void PersistableMessage::blockContentRelease() -{ - contentReleaseState.blocked = true; -} -bool PersistableMessage::checkContentReleasable() -{ - return contentReleaseState.requested && !contentReleaseState.blocked; -} +void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr, AsyncStore*) {} -bool PersistableMessage::isContentReleaseBlocked() -{ - return contentReleaseState.blocked; -} - -bool PersistableMessage::isContentReleaseRequested() -{ - return contentReleaseState.requested; -} +bool PersistableMessage::isDequeueComplete() { return false; } +void PersistableMessage::dequeueComplete() {} }} diff --git a/cpp/src/qpid/broker/PersistableMessage.h b/cpp/src/qpid/broker/PersistableMessage.h index 8823cfa638..be2910280c 100644 --- a/cpp/src/qpid/broker/PersistableMessage.h +++ b/cpp/src/qpid/broker/PersistableMessage.h @@ -24,30 +24,32 @@ #include <string> #include <list> -#include <boost/shared_ptr.hpp> -#include <boost/weak_ptr.hpp> +#include <map> +#include <boost/intrusive_ptr.hpp> #include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/Persistable.h" #include "qpid/framing/amqp_types.h" +#include "qpid/framing/amqp_framing.h" #include "qpid/sys/Mutex.h" #include "qpid/broker/PersistableQueue.h" #include "qpid/broker/AsyncCompletion.h" +#include "qpid/broker/MessageHandle.h" namespace qpid { +namespace types { +class Variant; +} namespace broker { class MessageStore; class AsyncStore; +class Queue; /** * Base class for persistable messages. */ class PersistableMessage : public Persistable { - typedef std::list< boost::weak_ptr<PersistableQueue> > syncList; - sys::Mutex asyncDequeueLock; - sys::Mutex storeLock; - /** * "Ingress" messages == messages sent _to_ the broker. * Tracks the number of outstanding asynchronous operations that must @@ -57,72 +59,26 @@ class PersistableMessage : public Persistable * operations have completed, the transfer of this message from the client * may be considered complete. */ - AsyncCompletion ingressCompletion; - - /** - * Tracks the number of outstanding asynchronous dequeue - * operations. When the message is dequeued asynchronously the - * count is incremented; when that dequeue completes it is - * decremented. Thus when it is 0, there are no outstanding - * dequeues. - */ - int asyncDequeueCounter; - - void dequeueAsync(); - - syncList synclist; - struct ContentReleaseState - { - bool blocked; - bool requested; - bool released; - - ContentReleaseState(); - }; - ContentReleaseState contentReleaseState; - - protected: - /** Called when all dequeues are complete for this message. */ - virtual void allDequeuesComplete() = 0; - - void setContentReleased(); - - MessageStore* store; // deprecated, use AsyncStore - AsyncStore* asyncStore; // new AsyncStore interface - + AsyncCompletion* ingressCompletion; + boost::intrusive_ptr<AsyncCompletion> holder; + mutable uint64_t persistenceId; + MessageHandle msgHandle; public: - typedef boost::shared_ptr<PersistableMessage> shared_ptr; - - /** - * @returns the size of the headers when encoded - */ - virtual uint32_t encodedHeaderSize() const = 0; - - virtual ~PersistableMessage(); - PersistableMessage(); + virtual ~PersistableMessage(); void flush(); - - QPID_BROKER_EXTERN bool isContentReleased() const; - - QPID_BROKER_EXTERN void setStore(MessageStore*); // deprecated - QPID_BROKER_EXTERN void setStore(AsyncStore*); - void requestContentRelease(); - void blockContentRelease(); - bool checkContentReleasable(); - bool isContentReleaseBlocked(); - bool isContentReleaseRequested(); virtual QPID_BROKER_EXTERN bool isPersistent() const = 0; /** track the progress of a message received by the broker - see ingressCompletion above */ - QPID_BROKER_INLINE_EXTERN bool isIngressComplete() { return ingressCompletion.isDone(); } - QPID_BROKER_INLINE_EXTERN AsyncCompletion& getIngressCompletion() { return ingressCompletion; } + QPID_BROKER_INLINE_EXTERN bool isIngressComplete() { return ingressCompletion->isDone(); } + QPID_BROKER_INLINE_EXTERN AsyncCompletion& getIngressCompletion() { return *ingressCompletion; } + QPID_BROKER_EXTERN void setIngressCompletion(boost::intrusive_ptr<AsyncCompletion> i); - QPID_BROKER_INLINE_EXTERN void enqueueStart() { ingressCompletion.startCompleter(); } - QPID_BROKER_INLINE_EXTERN void enqueueComplete() { ingressCompletion.finishCompleter(); } + QPID_BROKER_INLINE_EXTERN void enqueueStart() { ingressCompletion->startCompleter(); } + QPID_BROKER_INLINE_EXTERN void enqueueComplete() { ingressCompletion->finishCompleter(); } QPID_BROKER_EXTERN void enqueueAsync(PersistableQueue::shared_ptr queue, // deprecated MessageStore* _store); @@ -131,18 +87,23 @@ class PersistableMessage : public Persistable QPID_BROKER_EXTERN bool isDequeueComplete(); - QPID_BROKER_EXTERN void dequeueComplete(); - QPID_BROKER_EXTERN void dequeueAsync(PersistableQueue::shared_ptr queue, // deprecated MessageStore* _store); QPID_BROKER_EXTERN void dequeueAsync(PersistableQueue::shared_ptr queue, AsyncStore* _store); - bool isStoredOnQueue(PersistableQueue::shared_ptr queue); - - void addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store); // deprecated - void addToSyncList(PersistableQueue::shared_ptr queue, AsyncStore* _store); + uint64_t getPersistenceId() const { return persistenceId; } + void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; } + + MessageHandle& getMessageHandle() { return msgHandle; } + const MessageHandle& getMessagehandle() const { return msgHandle; } + + + virtual void decodeHeader(framing::Buffer& buffer) = 0; + virtual void decodeContent(framing::Buffer& buffer) = 0; + virtual uint32_t encodedHeaderSize() const = 0; + virtual boost::intrusive_ptr<PersistableMessage> merge(const std::map<std::string, qpid::types::Variant>& annotations) const = 0; }; }} diff --git a/cpp/src/qpid/broker/PriorityQueue.cpp b/cpp/src/qpid/broker/PriorityQueue.cpp index 9a0fead744..99488ded13 100644 --- a/cpp/src/qpid/broker/PriorityQueue.cpp +++ b/cpp/src/qpid/broker/PriorityQueue.cpp @@ -19,24 +19,53 @@ * */ #include "qpid/broker/PriorityQueue.h" +#include "qpid/broker/Message.h" #include "qpid/broker/Queue.h" -#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/QueueCursor.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" +#include <algorithm> #include <cmath> +#include <boost/bind.hpp> namespace qpid { namespace broker { +namespace { +class PriorityContext : public CursorContext { + public: + std::vector<QueueCursor> position; + PriorityContext(size_t levels, SubscriptionType type) : position(levels, QueueCursor(type)) {} +}; +} + PriorityQueue::PriorityQueue(int l) : levels(l), - messages(levels, Deque()), - frontLevel(0), haveFront(false), cached(false) {} + messages(levels, Deque(boost::bind(&PriorityQueue::priorityPadding, this, _1))), + counters(levels, framing::SequenceNumber()), + fifo(boost::bind(&PriorityQueue::fifoPadding, this, _1)), + frontLevel(0), haveFront(false), cached(false) +{ +} -bool PriorityQueue::deleted(const QueuedMessage& qm) { - bool deleted = fifo.deleted(qm); - if (deleted) erase(qm); - return deleted; +bool PriorityQueue::deleted(const QueueCursor& c) +{ + MessagePointer* ptr = fifo.find(c); + if (ptr && ptr->holder) { + //mark the message as deleted + ptr->holder->message.setState(DELETED); + //clean the deque for the relevant priority level + boost::shared_ptr<PriorityContext> ctxt = boost::dynamic_pointer_cast<PriorityContext>(c.context); + messages[ptr->holder->priority].clean(); + //stop referencing that message holder (it may now have been + //deleted) + ptr->holder = 0; + //clean fifo index + fifo.clean(); + return true; + } else { + return false; + } } size_t PriorityQueue::size() @@ -44,85 +73,69 @@ size_t PriorityQueue::size() return fifo.size(); } -namespace { -bool before(QueuedMessage* a, QueuedMessage* b) { return *a < *b; } -} - -void PriorityQueue::release(const QueuedMessage& message) -{ - 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(); +Message* PriorityQueue::next(QueueCursor& cursor) +{ + boost::shared_ptr<PriorityContext> ctxt = boost::dynamic_pointer_cast<PriorityContext>(cursor.context); + if (!ctxt) { + ctxt = boost::shared_ptr<PriorityContext>(new PriorityContext(levels, CONSUMER)); + cursor.context = ctxt; } -} - - -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; + if (cursor.type == REPLICATOR) { + //browse in fifo order + MessagePointer* ptr = fifo.next(cursor); + return ptr ? &(ptr->holder->message) : 0; + } else if (cursor.type == PURGE) { + //iterate over message in reverse priority order (i.e. purge lowest priority message first) + //ignore any fairshare configuration here as well + for (int p = 0; p < levels; ++p) { + MessageHolder* holder = messages[p].next(ctxt->position[p]); + if (holder) { + cursor.setPosition(holder->message.getSequence(), 0); + return &(holder->message); + } } + return 0; + } else { + //check each level in turn, in priority order, for any more messages + Priority p = firstLevel(); + do { + MessageHolder* holder = messages[p.current].next(ctxt->position[p.current]); + if (holder) { + cursor.setPosition(holder->message.getSequence(), 0); + return &(holder->message); + } + } while (nextLevel(p)); + return 0; } } -bool PriorityQueue::acquire(const framing::SequenceNumber& position, QueuedMessage& message) +Message* PriorityQueue::find(const QueueCursor& cursor) { - bool acquired = fifo.acquire(position, message); - if (acquired) erase(message); // No longer available - return acquired; + return find(cursor.position, 0); } -bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message) +Message* PriorityQueue::find(const framing::SequenceNumber& position, QueueCursor* cursor) { - return fifo.find(position, message); + MessagePointer* ptr = fifo.find(position, cursor); + return ptr ? &(ptr->holder->message) : 0; } -bool PriorityQueue::browse( - const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) +void PriorityQueue::publish(const Message& published) { - return fifo.browse(position, message, unacquired); + MessageHolder holder; + holder.message = published; + holder.priority = getPriorityLevel(published); + holder.id = ++(counters[holder.priority]); + MessagePointer pointer; + pointer.holder = &(messages[holder.priority].publish(holder)); + pointer.id = published.getSequence(); + fifo.publish(pointer); } -bool PriorityQueue::consume(QueuedMessage& message) -{ - if (checkFront()) { - QueuedMessage* pm = messages[frontLevel].front(); - messages[frontLevel].pop_front(); - clearCache(); - pm->status = QueuedMessage::ACQUIRED; // Updates FIFO index - message = *pm; - return true; - } else { - return false; - } -} - -bool PriorityQueue::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) +Message* PriorityQueue::release(const QueueCursor& cursor) { - 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 -} - -void PriorityQueue::updateAcquired(const QueuedMessage& acquired) { - fifo.updateAcquired(acquired); -} - -void PriorityQueue::setPosition(const framing::SequenceNumber& n) { - fifo.setPosition(n); + MessagePointer* ptr = fifo.release(cursor); + return ptr ? &(ptr->holder->message) : 0; } void PriorityQueue::foreach(Functor f) @@ -130,62 +143,87 @@ void PriorityQueue::foreach(Functor 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)) { - (*i)->status = QueuedMessage::DELETED; // Updates fifo index - i = messages[priority].erase(i); - clearCache(); - } else { - ++i; - } - } - } - fifo.clean(); -} - -uint PriorityQueue::getPriorityLevel(const QueuedMessage& m) const +uint PriorityQueue::getPriorityLevel(const Message& m) const { - uint priority = m.payload->getPriority(); + uint priority = m.getPriority(); //Use AMQP 0-10 approach to mapping priorities to a fixed level //(see rule priority-level-implementation) const uint firstLevel = 5 - uint(std::min(5.0, std::ceil((double) levels/2.0))); if (priority <= firstLevel) return 0; return std::min(priority - firstLevel, (uint)levels-1); } +PriorityQueue::MessagePointer PriorityQueue::fifoPadding(qpid::framing::SequenceNumber id) +{ + PriorityQueue::MessagePointer pointer; + pointer.holder = 0; + pointer.id = id; + return pointer; +} -void PriorityQueue::clearCache() +PriorityQueue::MessageHolder PriorityQueue::priorityPadding(qpid::framing::SequenceNumber id) { - cached = false; + PriorityQueue::MessageHolder holder; + holder.id = id; + holder.message.setState(DELETED); + return holder; } -bool PriorityQueue::findFrontLevel(uint& l, PriorityLevels& m) +PriorityQueue::Priority PriorityQueue::firstLevel() { - for (int p = levels-1; p >= 0; --p) { - if (!m[p].empty()) { - l = p; - return true; - } + return Priority(levels - 1); +} +bool PriorityQueue::nextLevel(Priority& p) +{ + if (p.current > 0) { + --(p.current); + return true; + } else { + return false; } - return false; } -bool PriorityQueue::checkFront() +framing::SequenceNumber PriorityQueue::MessageHolder::getSequence() const +{ + return id; +} +void PriorityQueue::MessageHolder::setState(MessageState s) +{ + message.setState(s); +} +MessageState PriorityQueue::MessageHolder::getState() const +{ + return message.getState(); +} +PriorityQueue::MessageHolder::operator Message&() +{ + return message; +} +framing::SequenceNumber PriorityQueue::MessagePointer::getSequence() const { - if (!cached) { - haveFront = findFrontLevel(frontLevel, messages); - cached = true; + if (holder) { + return holder->message.getSequence(); + } else { + //this is used when the instance is merely acting as padding + return id; } - return haveFront; } - -uint PriorityQueue::getPriority(const QueuedMessage& message) +void PriorityQueue::MessagePointer::setState(MessageState s) { - const PriorityQueue* queue = dynamic_cast<const PriorityQueue*>(&(message.queue->getMessages())); - if (queue) return queue->getPriorityLevel(message); - else return 0; + if (holder) { + holder->message.setState(s); + } +} +MessageState PriorityQueue::MessagePointer::getState() const +{ + if (holder) { + return holder->message.getState(); + } else { + return DELETED; + } +} +PriorityQueue::MessagePointer::operator Message&() +{ + assert(holder); + return holder->message; } - }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/PriorityQueue.h b/cpp/src/qpid/broker/PriorityQueue.h index 301367358b..16432bfb54 100644 --- a/cpp/src/qpid/broker/PriorityQueue.h +++ b/cpp/src/qpid/broker/PriorityQueue.h @@ -22,6 +22,7 @@ * */ #include "qpid/broker/MessageDeque.h" +#include "qpid/broker/IndexedDeque.h" #include "qpid/sys/IntegerTypes.h" #include <deque> #include <vector> @@ -44,42 +45,63 @@ class PriorityQueue : public Messages virtual ~PriorityQueue() {} size_t size(); - bool deleted(const QueuedMessage&); - void release(const QueuedMessage&); - bool acquire(const framing::SequenceNumber&, QueuedMessage&); - bool find(const framing::SequenceNumber&, QueuedMessage&); - bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); - bool consume(QueuedMessage&); - bool push(const QueuedMessage& added, QueuedMessage& removed); - void updateAcquired(const QueuedMessage& acquired); - void setPosition(const framing::SequenceNumber&); - void foreach(Functor); - void removeIf(Predicate); - - static uint getPriority(const QueuedMessage&); + bool deleted(const QueueCursor&); + void publish(const Message& added); + Message* next(QueueCursor&); + Message* release(const QueueCursor& cursor); + Message* find(const QueueCursor&); + Message* find(const framing::SequenceNumber&, QueueCursor*); + void foreach(Functor); + static uint getPriority(const Message&); protected: - typedef std::deque<QueuedMessage*> Deque; - typedef std::vector<Deque> PriorityLevels; - virtual bool findFrontLevel(uint& p, PriorityLevels&); - const int levels; + struct Priority + { + const int start; + int current; + Priority(int s) : start(s), current(start) {} + }; + virtual Priority firstLevel(); + virtual bool nextLevel(Priority& ); private: - /** Available messages separated by priority and sorted in priority order. - * Holds pointers to the QueuedMessages in fifo + struct MessageHolder + { + Message message; + int priority; + framing::SequenceNumber id; + framing::SequenceNumber getSequence() const; + void setState(MessageState); + MessageState getState() const; + operator Message&(); + }; + struct MessagePointer + { + MessageHolder* holder; + framing::SequenceNumber id;//used only for padding + framing::SequenceNumber getSequence() const; + void setState(MessageState); + MessageState getState() const; + operator Message&(); + }; + typedef IndexedDeque<MessageHolder> Deque; + typedef std::vector<Deque> PriorityLevels; + typedef std::vector<framing::SequenceNumber> Counters; + + /** Holds pointers to messages (stored in the fifo index) separated by priority. */ PriorityLevels messages; - /** FIFO index of all messsagse (including acquired messages) for fast browsing and indexing */ - MessageDeque fifo; + Counters counters; + /** FIFO index of messages for fast browsing and indexing */ + IndexedDeque<MessagePointer> fifo; uint frontLevel; bool haveFront; bool cached; - void erase(const QueuedMessage&); - uint getPriorityLevel(const QueuedMessage&) const; - void clearCache(); - bool checkFront(); + uint getPriorityLevel(const Message&) const; + MessageHolder priorityPadding(qpid::framing::SequenceNumber); + MessagePointer fifoPadding(qpid::framing::SequenceNumber); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp index d5267c78dc..0dd4cb7b10 100644 --- a/cpp/src/qpid/broker/Queue.cpp +++ b/cpp/src/qpid/broker/Queue.cpp @@ -20,23 +20,23 @@ */ #include "qpid/broker/Queue.h" - #include "qpid/broker/Broker.h" -#include "qpid/broker/QueueEvents.h" +#include "qpid/broker/QueueCursor.h" +#include "qpid/broker/QueueDepth.h" +#include "qpid/broker/QueueSettings.h" #include "qpid/broker/Exchange.h" -#include "qpid/broker/Fairshare.h" #include "qpid/broker/DeliverableMessage.h" -#include "qpid/broker/LegacyLVQ.h" -#include "qpid/broker/MessageDeque.h" -#include "qpid/broker/MessageMap.h" #include "qpid/broker/MessageStore.h" +#include "qpid/broker/MessageDeque.h" +#include "qpid/broker/MessageDistributor.h" +#include "qpid/broker/FifoDistributor.h" #include "qpid/broker/NullMessageStore.h" #include "qpid/broker/QueueRegistry.h" -#include "qpid/broker/QueueFlowLimit.h" -#include "qpid/broker/ThresholdAlerts.h" -#include "qpid/broker/FifoDistributor.h" -#include "qpid/broker/MessageGroupManager.h" +//TODO: get rid of this +#include "qpid/broker/amqp_0_10/MessageTransfer.h" + +#include "qpid/amqp_0_10/Codecs.h" #include "qpid/StringUtils.h" #include "qpid/log/Statement.h" #include "qpid/management/ManagementAgent.h" @@ -76,26 +76,8 @@ namespace _qmf = qmf::org::apache::qpid::broker; namespace { -const std::string qpidMaxSize("qpid.max_size"); -const std::string qpidMaxCount("qpid.max_count"); -const std::string qpidNoLocal("no-local"); -const std::string qpidTraceIdentity("qpid.trace.id"); -const std::string qpidTraceExclude("qpid.trace.exclude"); -const std::string qpidLastValueQueueKey("qpid.last_value_queue_key"); -const std::string qpidLastValueQueue("qpid.last_value_queue"); -const std::string qpidLastValueQueueNoBrowse("qpid.last_value_queue_no_browse"); -const std::string qpidPersistLastNode("qpid.persist_last_node"); -const std::string qpidVQMatchProperty("qpid.LVQ_key"); -const std::string qpidQueueEventGeneration("qpid.queue_event_generation"); -const std::string qpidAutoDeleteTimeout("qpid.auto_delete_timeout"); -//following feature is not ready for general use as it doesn't handle -//the case where a message is enqueued on more than one queue well enough: -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, + +inline void mgntEnqStats(const Message& msg, _qmf::Queue* mgmtObject, _qmf::Broker* brokerMgmtObject) { @@ -103,12 +85,12 @@ inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg, _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); - uint64_t contentSize = msg->contentSize(); + uint64_t contentSize = msg.getContentSize(); qStats->msgTotalEnqueues +=1; bStats->msgTotalEnqueues += 1; qStats->byteTotalEnqueues += contentSize; bStats->byteTotalEnqueues += contentSize; - if (msg->isPersistent ()) { + if (msg.isPersistent ()) { qStats->msgPersistEnqueues += 1; bStats->msgPersistEnqueues += 1; qStats->bytePersistEnqueues += contentSize; @@ -119,20 +101,20 @@ inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg, } } -inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg, +inline void mgntDeqStats(const 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(); + uint64_t contentSize = msg.getContentSize(); qStats->msgTotalDequeues += 1; bStats->msgTotalDequeues += 1; qStats->byteTotalDequeues += contentSize; bStats->byteTotalDequeues += contentSize; - if (msg->isPersistent ()){ + if (msg.isPersistent ()){ qStats->msgPersistDequeues += 1; bStats->msgPersistDequeues += 1; qStats->bytePersistDequeues += contentSize; @@ -143,43 +125,81 @@ inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg, } } -} // namespace +QueueSettings merge(const QueueSettings& inputs, const Broker::Options& globalOptions) +{ + QueueSettings settings(inputs); + if (!settings.maxDepth.hasSize() && globalOptions.queueLimit) { + settings.maxDepth.setSize(globalOptions.queueLimit); + } + return settings; +} + +} -Queue::Queue(const string& _name, bool _autodelete, +Queue::TxPublish::TxPublish(const Message& m, boost::shared_ptr<Queue> q) : message(m), queue(q), prepared(false) {} +bool Queue::TxPublish::prepare(TransactionContext* ctxt) throw() +{ + try { + prepared = queue->enqueue(ctxt, message); + return true; + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to prepare: " << e.what()); + return false; + } +} +void Queue::TxPublish::commit() throw() +{ + try { + if (prepared) queue->process(message); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } +} +void Queue::TxPublish::rollback() throw() +{ + try { + if (prepared) queue->enqueueAborted(message); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to rollback: " << e.what()); + } +} + +Queue::Queue(const string& _name, const QueueSettings& _settings, MessageStore* const _store, - const OwnershipToken* const _owner, Manageable* parent, Broker* b) : name(_name), - autodelete(_autodelete), store(_store), - owner(_owner), + owner(0), consumerCount(0), browserCount(0), exclusive(0), - noLocal(false), persistLastNode(false), inLastNodeFailure(false), messages(new MessageDeque()), persistenceId(0), - policyExceeded(false), + settings(b ? merge(_settings, b->getOptions()) : _settings), mgmtObject(0), brokerMgmtObject(0), eventMode(0), - insertSeqNo(0), broker(b), deleted(false), barrier(*this), - autoDeleteTimeout(0), allocator(new FifoDistributor( *messages )) { + if (settings.maxDepth.hasCount()) current.setCount(0); + if (settings.maxDepth.hasSize()) current.setSize(0); + if (settings.traceExcludes.size()) { + split(traceExclude, settings.traceExcludes, ", "); + } + qpid::amqp_0_10::translate(settings.asMap(), encodableSettings); if (parent != 0 && broker != 0) { ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete); - mgmtObject->set_exclusive(_owner != 0); + mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, settings.autodelete); + mgmtObject->set_arguments(settings.asMap()); agent->addObject(mgmtObject, 0, store != 0); brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject(); if (brokerMgmtObject) @@ -197,32 +217,36 @@ Queue::~Queue() } } -bool isLocalTo(const OwnershipToken* token, boost::intrusive_ptr<Message>& msg) +bool isLocalTo(const OwnershipToken* token, const Message& msg) { - return token && token->isLocal(msg->getPublisher()); + return token && token->isLocal(msg.getPublisher()); } -bool Queue::isLocal(boost::intrusive_ptr<Message>& msg) +bool Queue::isLocal(const Message& msg) { //message is considered local if it was published on the same //connection as that of the session which declared this queue //exclusive (owner) or which has an exclusive subscription //(exclusive) - return noLocal && (isLocalTo(owner, msg) || isLocalTo(exclusive, msg)); + return settings.noLocal && (isLocalTo(owner, msg) || isLocalTo(exclusive, msg)); } -bool Queue::isExcluded(boost::intrusive_ptr<Message>& msg) +bool Queue::isExcluded(const Message& msg) { - return traceExclude.size() && msg->isExcluded(traceExclude); + return traceExclude.size() && msg.isExcluded(traceExclude); } -void Queue::deliver(boost::intrusive_ptr<Message> msg){ +void Queue::deliver(Message msg, TxBuffer* txn){ + //TODO: move some of this out of the queue and into the publishing + //'link' for whatever protocol is used; that would let protocol + //specific stuff be kept out the queue + // Check for deferred delivery in a cluster. if (broker && broker->deferDelivery(name, msg)) return; - if (msg->isImmediate() && getConsumerCount() == 0) { + if (broker::amqp_0_10::MessageTransfer::isImmediateDeliveryRequired(msg) && getConsumerCount() == 0) { if (alternateExchange) { - DeliverableMessage deliverable(msg); + DeliverableMessage deliverable(msg, 0); alternateExchange->route(deliverable); } } else if (isLocal(msg)) { @@ -232,47 +256,38 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){ //drop message QPID_LOG(info, "Dropping excluded message from " << getName()); } else { - enqueue(0, msg); - push(msg); - QPID_LOG(debug, "Message " << msg << " enqueued on " << name); + if (txn) { + TxOp::shared_ptr op(new TxPublish(msg, shared_from_this())); + txn->enlist(op); + } else { + if (enqueue(0, msg)) { + push(msg); + QPID_LOG(debug, "Message " << msg << " enqueued on " << name); + } else { + QPID_LOG(debug, "Message " << msg << " dropped from " << name); + } + } } } -void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg) +void Queue::recoverPrepared(const Message& msg) { Mutex::ScopedLock locker(messageLock); - if (policy.get()) policy->recoverEnqueued(msg); + current += QueueDepth(1, msg.getContentSize()); } -void Queue::recover(boost::intrusive_ptr<Message>& msg) +void Queue::recover(Message& msg) { - { - Mutex::ScopedLock locker(messageLock); - if (policy.get()) policy->recoverEnqueued(msg); - } - + recoverPrepared(msg); push(msg, true); - if (store){ - // setup synclist for recovered messages, so they don't get re-stored on lastNodeFailure - msg->addToSyncList(shared_from_this(), store); - } - - if (store && (!msg->isContentLoaded() || msg->checkContentReleasable())) { - //content has not been loaded, need to ensure that lazy loading mode is set: - //TODO: find a nicer way to do this - msg->releaseContent(store); - // NOTE: The log message in this section are used for flow-to-disk testing (which checks the log for the - // presence of this message). Do not change this without also checking these tests. - QPID_LOG(debug, "Message id=\"" << msg->getProperties<MessageProperties>()->getMessageId() << "\"; pid=0x" << - std::hex << msg->getPersistenceId() << std::dec << ": Content released after recovery"); - } } -void Queue::process(boost::intrusive_ptr<Message>& msg){ +void Queue::process(Message& msg) +{ push(msg); if (mgmtObject != 0){ _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); - const uint64_t contentSize = msg->contentSize(); + const uint64_t contentSize = msg.getContentSize(); qStats->msgTxnEnqueues += 1; qStats->byteTxnEnqueues += contentSize; mgmtObject->statisticsUpdated(); @@ -285,46 +300,22 @@ void Queue::process(boost::intrusive_ptr<Message>& msg){ } } -void Queue::requeue(const QueuedMessage& msg){ +void Queue::release(const QueueCursor& position, bool markRedelivered) +{ assertClusterSafe(); QueueListeners::NotificationSet copy; { - if (!isEnqueued(msg)) return; - if (deleted) { - // - // If the queue has been deleted, requeued messages must be sent to the alternate exchange - // if one is configured. - // - if (alternateExchange.get()) { - DeliverableMessage dmsg(msg.payload); - alternateExchange->routeWithAlternate(dmsg); - if (brokerMgmtObject) - brokerMgmtObject->inc_abandonedViaAlt(); - } else { - if (brokerMgmtObject) - brokerMgmtObject->inc_abandoned(); - } - mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); - } else { - { - Mutex::ScopedLock locker(messageLock); - messages->release(msg); - observeRequeue(msg, locker); + Mutex::ScopedLock locker(messageLock); + if (!deleted) { + Message* message = messages->release(position); + if (message) { + if (!markRedelivered) message->undeliver(); 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())) { - msg.payload->forcePersistent(); - if (msg.payload->isForcedPersistent() ){ - boost::intrusive_ptr<Message> payload = msg.payload; - enqueue(0, payload); + observeRequeue(*message, locker); + if (mgmtObject) { + mgmtObject->inc_releases(); + if (brokerMgmtObject) + brokerMgmtObject->inc_releases(); } } } @@ -332,163 +323,118 @@ void Queue::requeue(const QueuedMessage& msg){ copy.notify(); } -bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message) +bool Queue::dequeueMessageAt(const SequenceNumber& position) { - assertClusterSafe(); - QPID_LOG(debug, "Attempting to acquire message at " << position); - if (acquire(position, message)) { - QPID_LOG(debug, "Acquired message at " << position << " from " << name); - return true; - } else { - QPID_LOG(debug, "Could not acquire message at " << position << " from " << name << "; no message at that position"); - return false; - } -} - -bool Queue::acquire(const QueuedMessage& msg, const std::string& consumer) -{ - assertClusterSafe(); - QPID_LOG(debug, consumer << " attempting to acquire message at " << msg.position); - bool ok; + boost::intrusive_ptr<PersistableMessage> pmsg; { 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)) { - QPID_LOG(debug, "Acquired message at " << msg.position << " from " << name); - return true; + assertClusterSafe(); + QPID_LOG(debug, "Attempting to dequeue message at " << position); + QueueCursor cursor; + Message* msg = messages->find(position, &cursor); + if (msg) { + if (msg->isPersistent()) pmsg = msg->getPersistentContext(); + observeDequeue(*msg, locker); + messages->deleted(cursor); + } else { + QPID_LOG(debug, "Could not dequeue message at " << position << "; no such message"); + return false; + } } - QPID_LOG(debug, "Could not acquire message at " << msg.position << " from " << name << "; no message at that position"); - return false; + dequeueFromStore(pmsg); + return true; } -void Queue::notifyListener() +bool Queue::acquire(const QueueCursor& position, const std::string& consumer) { + Mutex::ScopedLock locker(messageLock); assertClusterSafe(); - QueueListeners::NotificationSet set; - { - Mutex::ScopedLock locker(messageLock); - if (messages->size()) { - listeners.populate(set); - } - } - set.notify(); -} + Message* msg; -bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) -{ - checkNotDeleted(c); - if (c->preAcquires()) { - switch (consumeNextMessage(m, c)) { - case CONSUMED: - return true; - case CANT_CONSUME: - notifyListener();//let someone else try - case NO_MESSAGES: - default: + msg = messages->find(position); + if (msg) { + QPID_LOG(debug, consumer << " attempting to acquire message at " << msg->getSequence()); + if (!allocator->acquire(consumer, *msg)) { + QPID_LOG(debug, "Not permitted to acquire msg at " << msg->getSequence() << " from '" << name); return false; + } else { + observeAcquire(*msg, locker); + QPID_LOG(debug, "Acquired message at " << msg->getSequence() << " from " << name); + return true; } } else { - return browseNextMessage(m, c); + QPID_LOG(debug, "Failed to acquire message which no longer exists on " << name); + return false; } } -Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) +bool Queue::getNextMessage(Message& m, Consumer::shared_ptr& c) { + checkNotDeleted(c); + QueueListeners::NotificationSet set; while (true) { - QueuedMessage msg; - 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; - } - - 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)) { - { - Mutex::ScopedLock locker(messageLock); - bool ok = allocator->allocate( c->getName(), msg ); // inform allocator - (void) ok; assert(ok); - observeAcquire(msg, locker); - } + //TODO: reduce lock scope + Mutex::ScopedLock locker(messageLock); + Message* msg = messages->next(*c); + if (msg) { + if (msg->hasExpired()) { + QPID_LOG(debug, "Message expired from queue '" << name << "'"); + observeDequeue(*msg, locker); + //ERROR: don't hold lock across call to store!! + if (msg->isPersistent()) dequeueFromStore(msg->getPersistentContext()); if (mgmtObject) { - mgmtObject->inc_acquires(); + mgmtObject->inc_discardsTtl(); if (brokerMgmtObject) - brokerMgmtObject->inc_acquires(); + brokerMgmtObject->inc_discardsTtl(); } - 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->deleted(*c); + continue; } - } else { - //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) { - QueuedMessage msg; - 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 << "'"); - return false; - } - - if (c->filter(msg.payload) && !msg.payload->hasExpired()) { - if (c->accept(msg.payload)) { - //consumer wants the message - c->setPosition(msg.position); - m = msg; - return true; + if (c->filter(*msg)) { + if (c->accept(*msg)) { + if (c->preAcquires()) { + QPID_LOG(debug, "Attempting to acquire message " << msg << " from '" << name << "' with state " << msg->getState()); + if (allocator->acquire(c->getName(), *msg)) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } + observeAcquire(*msg, locker); + msg->deliver(); + } else { + QPID_LOG(debug, "Could not acquire message from '" << name << "'"); + continue; //try another message + } + } + QPID_LOG(debug, "Message retrieved from '" << name << "'"); + m = *msg; + return true; + } else { + //message(s) are available but consumer hasn't got enough credit + QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); + if (c->preAcquires()) { + //let someone else try + listeners.populate(set); + } + break; + } } else { - //browser hasn't got enough credit for the message - QPID_LOG(debug, "Browser can't currently accept message from '" << name << "'"); - return false; + //consumer will never want this message, try another one + QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); + if (c->preAcquires()) { + //let someone else try to take this one + listeners.populate(set); + } } } else { - //consumer will never want this message, continue seeking - QPID_LOG(debug, "Browser skipping message from '" << name << "'"); - c->setPosition(msg.position); + QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); + listeners.addListener(c); + return false; } } + set.notify(); return false; } @@ -507,23 +453,28 @@ void Queue::removeListener(Consumer::shared_ptr c) bool Queue::dispatch(Consumer::shared_ptr c) { - QueuedMessage msg(this); + Message msg; if (getNextMessage(msg, c)) { - c->deliver(msg); + c->deliver(*c, msg); return true; } else { return false; } } -bool Queue::find(SequenceNumber pos, QueuedMessage& msg) const { +bool Queue::find(SequenceNumber pos, Message& msg) const +{ Mutex::ScopedLock locker(messageLock); - if (messages->find(pos, msg)) + Message* ptr = messages->find(pos, 0); + if (ptr) { + msg = *ptr; return true; + } return false; } -void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ +void Queue::consume(Consumer::shared_ptr c, bool requestExclusive) +{ assertClusterSafe(); { Mutex::ScopedLock locker(messageLock); @@ -550,7 +501,7 @@ void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ browserCount++; consumerCount++; //reset auto deletion timer if necessary - if (autoDeleteTimeout && autoDeleteTask) { + if (settings.autoDeleteDelay && autoDeleteTask) { autoDeleteTask->cancel(); } observeConsumerAdd(*c, locker); @@ -559,7 +510,8 @@ void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ mgmtObject->inc_consumerCount (); } -void Queue::cancel(Consumer::shared_ptr c){ +void Queue::cancel(Consumer::shared_ptr c) +{ removeListener(c); { Mutex::ScopedLock locker(messageLock); @@ -572,65 +524,6 @@ void Queue::cancel(Consumer::shared_ptr c){ mgmtObject->dec_consumerCount (); } -QueuedMessage Queue::get(){ - QueuedMessage msg(this); - 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; -} - -namespace { -bool collectIf(QueuedMessage& qm, Messages::Predicate predicate, - std::deque<QueuedMessage>& collection) -{ - if (predicate(qm)) { - collection.push_back(qm); - return true; - } else { - return false; - } -} - -bool isExpired(const QueuedMessage& qm) { return qm.payload->hasExpired(); } -} // namespace - -void Queue::dequeueIf(Messages::Predicate predicate, - std::deque<QueuedMessage>& dequeued) -{ - { - Mutex::ScopedLock locker(messageLock); - messages->removeIf(boost::bind(&collectIf, _1, predicate, boost::ref(dequeued))); - } - if (!dequeued.empty()) { - if (mgmtObject) { - mgmtObject->inc_acquires(dequeued.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_acquires(dequeued.size()); - } - for (std::deque<QueuedMessage>::const_iterator i = dequeued.begin(); - i != dequeued.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 ); - } - } -} - /** *@param lapse: time since the last purgeExpired */ @@ -642,13 +535,17 @@ void Queue::purgeExpired(sys::Duration lapse) { dequeueSincePurge -= count; int seconds = int64_t(lapse)/qpid::sys::TIME_SEC; if (seconds == 0 || count / seconds < 1) { - std::deque<QueuedMessage> dequeued; - dequeueIf(boost::bind(&isExpired, _1), dequeued); - if (dequeued.size()) { - if (mgmtObject) { - mgmtObject->inc_discardsTtl(dequeued.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsTtl(dequeued.size()); + uint32_t count = remove(0, boost::bind(&Message::hasExpired, _1), 0, CONSUMER); + QPID_LOG(debug, "Purged " << count << " expired messages from " << getName()); + // + // Report the count of discarded-by-ttl messages + // + if (mgmtObject && count) { + mgmtObject->inc_acquires(count); + mgmtObject->inc_discardsTtl(count); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(count); + brokerMgmtObject->inc_discardsTtl(count); } } } @@ -663,7 +560,7 @@ namespace { static const std::string typeKey; static const std::string paramsKey; static MessageFilter *create( const ::qpid::types::Variant::Map *filter ); - virtual bool match( const QueuedMessage& ) const { return true; } + virtual bool match( const Message& ) const { return true; } virtual ~MessageFilter() {} protected: MessageFilter() {}; @@ -687,13 +584,9 @@ namespace { static const std::string valueKey; HeaderMatchFilter( const std::string& _header, const std::string& _value ) : MessageFilter (), header(_header), value(_value) {} - bool match( const QueuedMessage& msg ) const + bool match( const Message& msg ) const { - const qpid::framing::FieldTable* headers = msg.payload->getApplicationHeaders(); - if (!headers) return false; - FieldTable::ValuePtr h = headers->get(header); - if (!h || !h->convertsTo<std::string>()) return false; - return h->get<std::string>() == value; + return msg.getPropertyAsString(header) == value; } private: const std::string header; @@ -730,36 +623,68 @@ namespace { return new MessageFilter(); } - // used by removeIf() to collect all messages matching a filter, maximum match count is - // optional. - struct Collector { - const uint32_t maxMatches; - MessageFilter& filter; - std::deque<QueuedMessage> matches; - Collector(MessageFilter& filter, uint32_t max) - : maxMatches(max), filter(filter) {} - bool operator() (QueuedMessage& qm) - { - if (maxMatches == 0 || matches.size() < maxMatches) { - if (filter.match( qm )) { - matches.push_back(qm); - return true; - } - } + bool reroute(boost::shared_ptr<Exchange> e, const Message& m) + { + if (e) { + DeliverableMessage d(m, 0); + d.getMessage().clearTrace(); + e->routeWithAlternate(d); + return true; + } else { return false; } - }; - + } + void moveTo(boost::shared_ptr<Queue> q, Message& m) + { + if (q) { + q->deliver(m); + } + } } // end namespace +uint32_t Queue::remove(const uint32_t maxCount, MessagePredicate p, MessageFunctor f, SubscriptionType type) +{ + std::deque<Message> removed; + { + QueueCursor c(type); + uint32_t count(0); + Mutex::ScopedLock locker(messageLock); + Message* m = messages->next(c); + while (m){ + if (!p || p(*m)) { + if (!maxCount || count++ < maxCount) { + if (m->getState() == AVAILABLE) { + //don't actually acquire, just act as if we did + observeAcquire(*m, locker); + } + observeDequeue(*m, locker); + removed.push_back(*m);//takes a copy of the message + if (!messages->deleted(c)) { + QPID_LOG(warning, "Failed to correctly remove message from " << name << "; state is not consistent!"); + assert(false); + } + } else { + break; + } + } + m = messages->next(c); + } + } + for (std::deque<Message>::iterator i = removed.begin(); i != removed.end(); ++i) { + if (f) f(*i);//ERROR? need to clear old persistent context? + if (i->isPersistent()) dequeueFromStore(i->getPersistentContext());//do this outside of lock and after any re-routing + } + return removed.size(); +} + /** * purge - for purging all or some messages on a queue * depending on the purge_request * - * purge_request == 0 then purge all messages - * == N then purge N messages from queue - * Sometimes purge_request == 1 to unblock the top of queue + * qty == 0 then purge all messages + * == N then purge N messages from queue + * Sometimes qty == 1 to unblock the top of queue * * The dest exchange may be supplied to re-route messages through the exchange. * It is safe to re-route messages such that they arrive back on the same queue, @@ -768,172 +693,53 @@ namespace { * An optional filter can be supplied that will be applied against each message. The * message is purged only if the filter matches. See MessageDistributor for more detail. */ -uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> dest, +uint32_t Queue::purge(const uint32_t qty, boost::shared_ptr<Exchange> dest, const qpid::types::Variant::Map *filter) { 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) ); - } + uint32_t count = remove(qty, boost::bind(&MessageFilter::match, mf.get(), _1), boost::bind(&reroute, dest, _1), CONSUMER/*?*/); - 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) { - - { - // 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); + if (mgmtObject && count) { + mgmtObject->inc_acquires(count); + if (dest.get()) { + mgmtObject->inc_reroutes(count); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(count); + brokerMgmtObject->inc_reroutes(count); } - dequeue(0, *qmsg); - QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName()); - // now reroute if necessary - if (dest.get()) { - assert(qmsg->payload); - qmsg->payload->clearTrace(); - DeliverableMessage dmsg(qmsg->payload); - dest->routeWithAlternate(dmsg); + } else { + mgmtObject->inc_discardsPurge(count); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(count); + brokerMgmtObject->inc_discardsPurge(count); } } } - return c.matches.size(); + + return count; } uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty, const qpid::types::Variant::Map *filter) { std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter)); - Collector c(*mf.get(), qty); - - { - Mutex::ScopedLock locker(messageLock); - messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); - } - - - if (!c.matches.empty()) { - // Update observers and message state: - - 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(); + return remove(qty, boost::bind(&MessageFilter::match, mf.get(), _1), boost::bind(&moveTo, destq, _1), CONSUMER/*?*/); } -/** Acquire the message at the given position, return true and msg if acquire succeeds */ -bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg) +void Queue::push(Message& message, bool /*isRecovery*/) { - 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; - } - return false; -} - -void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){ assertClusterSafe(); QueueListeners::NotificationSet copy; - QueuedMessage removed, qm(this, msg); - bool dequeueRequired = false; { Mutex::ScopedLock locker(messageLock); - qm.position = ++sequence; - if (messages->push(qm, removed)) { - dequeueRequired = true; - observeAcquire(removed, locker); - } - observeEnqueue(qm, locker); - if (policy.get()) { - policy->enqueued(qm); - } + message.setSequence(++sequence); + messages->publish(message); listeners.populate(copy); - } - 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); - } + observeEnqueue(message, locker); } copy.notify(); } -void isEnqueueComplete(uint32_t* result, const QueuedMessage& message) -{ - if (message.payload->isIngressComplete()) (*result)++; -} - -/** function only provided for unit tests, or code not in critical message path */ -uint32_t Queue::getEnqueueCompleteMessageCount() const -{ - uint32_t count = 0; - Mutex::ScopedLock locker(messageLock); - messages->foreach(boost::bind(&isEnqueueComplete, &count, _1)); - return count; -} - uint32_t Queue::getMessageCount() const { Mutex::ScopedLock locker(messageLock); @@ -949,7 +755,7 @@ uint32_t Queue::getConsumerCount() const bool Queue::canAutoDelete() const { Mutex::ScopedLock locker(messageLock); - return autodelete && !consumerCount && !owner; + return settings.autodelete && !consumerCount && !owner; } void Queue::clearLastNodeFailure() @@ -957,14 +763,9 @@ void Queue::clearLastNodeFailure() inLastNodeFailure = false; } -void Queue::forcePersistent(QueuedMessage& message) +void Queue::forcePersistent(const Message& /*message*/) { - if(!message.payload->isStoredOnQueue(shared_from_this())) { - message.payload->forcePersistent(); - if (message.payload->isForcedPersistent() ){ - enqueue(0, message.payload); - } - } + //TODO } void Queue::setLastNodeFailure() @@ -982,153 +783,129 @@ void Queue::setLastNodeFailure() } -// return true if store exists, -bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck) +/* + * return true if enqueue succeeded and message should be made + * available; returning false will result in the message being dropped + */ +bool Queue::enqueue(TransactionContext* ctxt, Message& msg) { ScopedUse u(barrier); if (!u.acquired) return false; - if (policy.get() && !suppressPolicyCheck) { - std::deque<QueuedMessage> dequeues; - { - Mutex::ScopedLock locker(messageLock); - try { - policy->tryEnqueue(msg); - } catch(ResourceLimitExceededException&) { - if (mgmtObject) { - mgmtObject->inc_discardsOverflow(); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsOverflow(); - } - throw; - } - policy->getPendingDequeues(dequeues); - } - //depending on policy, may have some dequeues that need to performed without holding the lock - - // - // Count the dequeues as ring-discards. We know that these aren't rejects because - // policy->tryEnqueue would have thrown an exception. - // - if (mgmtObject && !dequeues.empty()) { - mgmtObject->inc_discardsRing(dequeues.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsRing(dequeues.size()); + { + Mutex::ScopedLock locker(messageLock); + if (!checkDepth(QueueDepth(1, msg.getContentSize()), msg)) { + return false; } - - for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); } if (inLastNodeFailure && persistLastNode){ - msg->forcePersistent(); + forcePersistent(msg); } - if (traceId.size()) { - msg->addTraceId(traceId); + if (settings.traceId.size()) { + msg.addTraceId(settings.traceId); } - if ((msg->isPersistent() || msg->checkContentReleasable()) && store) { + if (msg.isPersistent() && store) { // mark the message as being enqueued - the store MUST CALL msg->enqueueComplete() // when it considers the message stored. - msg->enqueueAsync(shared_from_this(), store); - boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg); + boost::intrusive_ptr<PersistableMessage> pmsg = msg.getPersistentContext(); + assert(pmsg); + pmsg->enqueueAsync(shared_from_this(), store); store->enqueue(ctxt, pmsg, *this); - return true; } - if (!store) { - //Messages enqueued on a transient queue should be prevented - //from having their content released as it may not be - //recoverable by these queue for delivery - msg->blockContentRelease(); - } - return false; + return true; } -void Queue::enqueueAborted(boost::intrusive_ptr<Message> msg) +void Queue::enqueueAborted(const Message& msg) { + //Called when any transactional enqueue is aborted (including but + //not limited to a recovered dtx transaction) Mutex::ScopedLock locker(messageLock); - if (policy.get()) policy->enqueueAborted(msg); + current -= QueueDepth(1, msg.getContentSize()); } -// return true if store exists, -bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) +void Queue::enqueueCommited(Message& 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); - } + //called when a recovered dtx enqueue operation is committed; the + //message is already on disk and space has been reserved in policy + //but it should now be made available + process(msg); +} +void Queue::dequeueAborted(Message& msg) +{ + //called when a recovered dtx dequeue operation is aborted; the + //message should be added back to the queue + push(msg); +} +void Queue::dequeueCommited(const Message& msg) +{ + //called when a recovered dtx dequeue operation is committed; the + //message will at this point have already been removed from the + //store and will not be available for delivery. The only action + //required is to ensure the observers are notified and the + //management stats are correctly decremented + Mutex::ScopedLock locker(messageLock); + observeDequeue(msg, locker); + if (mgmtObject != 0) { + mgmtObject->inc_msgTxnDequeues(); + mgmtObject->inc_byteTxnDequeues(msg.getContentSize()); } +} - 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(); - if (!fp || (fp && msg.payload->isStoredOnQueue(shared_from_this()))) { - if ((msg.payload->isPersistent() || msg.payload->checkContentReleasable()) && store) { - msg.payload->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue - boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg.payload); - store->dequeue(ctxt, pmsg, *this); - return true; - } +void Queue::dequeueFromStore(boost::intrusive_ptr<PersistableMessage> msg) +{ + ScopedUse u(barrier); + if (u.acquired && msg && store) { + store->dequeue(0, msg, *this); } - return false; } -void Queue::dequeueCommitted(const QueuedMessage& msg) +void Queue::dequeue(TransactionContext* ctxt, const QueueCursor& cursor) { + ScopedUse u(barrier); + if (!u.acquired) return; + boost::intrusive_ptr<PersistableMessage> pmsg; { Mutex::ScopedLock locker(messageLock); - if (policy.get()) policy->dequeued(msg); - messages->deleted(msg); - observeDequeue(msg, locker); + Message* msg = messages->find(cursor); + if (msg) { + if (msg->isPersistent()) pmsg = msg->getPersistentContext(); + if (!ctxt) { + observeDequeue(*msg, locker); + messages->deleted(cursor);//message pointer not valid after this + } + } else { + return; + } } - mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); - if (mgmtObject != 0) { - _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); - const uint64_t contentSize = msg.payload->contentSize(); - qStats->msgTxnDequeues += 1; - qStats->byteTxnDequeues += contentSize; - mgmtObject->statisticsUpdated(); + if (store && pmsg) { + store->dequeue(ctxt, pmsg, *this); + } +} + +void Queue::dequeueCommitted(const QueueCursor& cursor) +{ + Mutex::ScopedLock locker(messageLock); + Message* msg = messages->find(cursor); + if (msg) { + const uint64_t contentSize = msg->getContentSize(); + observeDequeue(*msg, locker); + if (mgmtObject != 0) { + mgmtObject->inc_msgTxnDequeues(); + mgmtObject->inc_byteTxnDequeues(contentSize); + } if (brokerMgmtObject) { _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); bStats->msgTxnDequeues += 1; bStats->byteTxnDequeues += contentSize; brokerMgmtObject->statisticsUpdated(); } - } -} - -/** - * 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) -{ - 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; + messages->deleted(cursor); } else { - return false; + QPID_LOG(error, "Could not find dequeued message on commit"); } } @@ -1136,8 +913,10 @@ bool Queue::popAndDequeue(QueuedMessage& msg) * Updates policy and management when a message has been dequeued, * Requires messageLock be held by caller. */ -void Queue::observeDequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) +void Queue::observeDequeue(const Message& msg, const Mutex::ScopedLock&) { + current -= QueueDepth(1, msg.getContentSize()); + mgntDeqStats(msg, mgmtObject, brokerMgmtObject); for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->dequeued(msg); @@ -1150,7 +929,7 @@ void Queue::observeDequeue(const QueuedMessage& msg, const qpid::sys::Mutex::Sco /** updates queue observers when a message has become unavailable for transfer. * Requires messageLock be held by caller. */ -void Queue::observeAcquire(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) +void Queue::observeAcquire(const Message& msg, const Mutex::ScopedLock&) { for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ @@ -1164,7 +943,7 @@ void Queue::observeAcquire(const QueuedMessage& msg, const qpid::sys::Mutex::Sco /** 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 qpid::sys::Mutex::ScopedLock&) +void Queue::observeRequeue(const Message& msg, const Mutex::ScopedLock&) { for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ @@ -1202,13 +981,11 @@ void Queue::observeConsumerRemove( const Consumer& c, const qpid::sys::Mutex::Sc } -void Queue::create(const FieldTable& _settings) +void Queue::create() { - settings = _settings; if (store) { - store->create(*this, _settings); + store->create(*this, settings.storeSettings); } - configureImpl(_settings); } @@ -1258,112 +1035,21 @@ bool getBoolSetting(const qpid::framing::FieldTable& settings, const std::string } } -void Queue::configure(const FieldTable& _settings) +void Queue::abandoned(const Message& message) { - settings = _settings; - configureImpl(settings); -} - -void Queue::configureImpl(const FieldTable& _settings) -{ - eventMode = _settings.getAsInt(qpidQueueEventGeneration); - if (eventMode && broker) { - broker->getQueueEvents().observe(*this, eventMode == ENQUEUE_ONLY); - } - - if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK && - (!store || NullMessageStore::isNullStore(store) || (broker && !(broker->getQueueEvents().isSync())) )) { - if ( NullMessageStore::isNullStore(store)) { - QPID_LOG(warning, "Flow to disk not valid for non-persisted queue:" << getName()); - } else if (broker && !(broker->getQueueEvents().isSync()) ) { - QPID_LOG(warning, "Flow to disk not valid with async Queue Events:" << getName()); - } - FieldTable copy(_settings); - copy.erase(QueuePolicy::typeKey); - setPolicy(QueuePolicy::createQueuePolicy(getName(), copy)); - } else { - setPolicy(QueuePolicy::createQueuePolicy(getName(), _settings)); - } - if (broker && broker->getManagementAgent()) { - ThresholdAlerts::observe(*this, *(broker->getManagementAgent()), _settings, broker->getOptions().queueThresholdEventRatio); - } - - //set this regardless of owner to allow use of no-local with exclusive consumers also - noLocal = getBoolSetting(_settings, qpidNoLocal); - QPID_LOG(debug, "Configured queue " << getName() << " with no-local=" << noLocal); - - std::string lvqKey = _settings.getAsString(qpidLastValueQueueKey); - if (lvqKey.size()) { - QPID_LOG(debug, "Configured queue " << getName() << " as Last Value Queue with key " << lvqKey); - messages = std::auto_ptr<Messages>(new MessageMap(lvqKey)); - allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages )); - } else if (getBoolSetting(_settings, qpidLastValueQueueNoBrowse)) { - QPID_LOG(debug, "Configured queue " << getName() << " as Legacy Last Value Queue with 'no-browse' on"); - messages = LegacyLVQ::updateOrReplace(messages, qpidVQMatchProperty, true, broker); - allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages )); - } else if (getBoolSetting(_settings, qpidLastValueQueue)) { - QPID_LOG(debug, "Configured queue " << getName() << " as Legacy Last Value Queue"); - messages = LegacyLVQ::updateOrReplace(messages, qpidVQMatchProperty, false, broker); - allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages )); - } else { - std::auto_ptr<Messages> m = Fairshare::create(_settings); - if (m.get()) { - messages = m; - allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages )); - QPID_LOG(debug, "Configured queue " << getName() << " as priority queue."); - } else { // default (FIFO) queue type - // override default message allocator if message groups configured. - boost::shared_ptr<MessageGroupManager> mgm(MessageGroupManager::create( getName(), *messages, _settings)); - if (mgm) { - allocator = mgm; - addObserver(mgm); - } - } - } - - persistLastNode = getBoolSetting(_settings, qpidPersistLastNode); - if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node for: " << getName()); - - traceId = _settings.getAsString(qpidTraceIdentity); - std::string excludeList = _settings.getAsString(qpidTraceExclude); - if (excludeList.size()) { - split(traceExclude, excludeList, ", "); - } - QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId - << "' and qpid.trace.exclude='"<< excludeList << "' i.e. " << traceExclude.size() << " elements"); - - FieldTable::ValuePtr p =_settings.get(qpidInsertSequenceNumbers); - if (p && p->convertsTo<std::string>()) insertSequenceNumbers(p->get<std::string>()); - - autoDeleteTimeout = getIntegerSetting(_settings, qpidAutoDeleteTimeout); - if (autoDeleteTimeout) - QPID_LOG(debug, "Configured queue " << getName() << " with qpid.auto_delete_timeout=" << autoDeleteTimeout); - - if (mgmtObject != 0) { - mgmtObject->set_arguments(ManagementAgent::toMap(_settings)); - } - - QueueFlowLimit::observe(*this, _settings); + if (reroute(alternateExchange, message) && brokerMgmtObject) + brokerMgmtObject->inc_abandonedViaAlt(); + else if (brokerMgmtObject) + brokerMgmtObject->inc_abandoned(); } void Queue::destroyed() { unbind(broker->getExchanges()); - - 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()) + remove(0, 0, boost::bind(&Queue::abandoned, this, _1), REPLICATOR/*even acquired message are treated as abandoned*/); + if (alternateExchange.get()) { alternateExchange->decAlternateUsers(); + } if (store) { barrier.destroy(); @@ -1401,20 +1087,6 @@ void Queue::unbind(ExchangeRegistry& exchanges) bindings.unbind(exchanges, shared_from_this()); } -void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) -{ - Mutex::ScopedLock locker(messageLock); - policy = _policy; - if (policy.get()) - policy->setQueue(this); -} - -const QueuePolicy* Queue::getPolicy() -{ - Mutex::ScopedLock locker(messageLock); - return policy.get(); -} - uint64_t Queue::getPersistenceId() const { return persistenceId; @@ -1434,10 +1106,7 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const void Queue::encode(Buffer& buffer) const { buffer.putShortString(name); - buffer.put(settings); - if (policy.get()) { - buffer.put(*policy); - } + buffer.put(encodableSettings); buffer.putShortString(alternateExchange.get() ? alternateExchange->getName() : std::string("")); } @@ -1445,21 +1114,19 @@ uint32_t Queue::encodedSize() const { return name.size() + 1/*short string size octet*/ + (alternateExchange.get() ? alternateExchange->getName().size() : 0) + 1 /* short string */ - + settings.encodedSize() - + (policy.get() ? (*policy).encodedSize() : 0); + + encodableSettings.encodedSize(); } Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer ) { string name; buffer.getShortString(name); - FieldTable settings; - buffer.get(settings); + FieldTable ft; + buffer.get(ft); boost::shared_ptr<Exchange> alternate; - std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true, false, 0, alternate, settings, true); - if (result.first->policy.get() && buffer.available() >= result.first->policy->encodedSize()) { - buffer.get ( *(result.first->policy) ); - } + QueueSettings settings(true, false); + settings.populate(ft, settings.storeSettings); + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, settings, alternate, true); if (buffer.available()) { string altExch; buffer.getShortString(altExch); @@ -1523,8 +1190,8 @@ struct AutoDeleteTask : qpid::sys::TimerTask void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue, const std::string& connectionId, const std::string& userId) { - if (queue->autoDeleteTimeout && queue->canAutoDelete()) { - AbsTime time(now(), Duration(queue->autoDeleteTimeout * TIME_SEC)); + if (queue->settings.autoDeleteDelay && queue->canAutoDelete()) { + AbsTime time(now(), Duration(queue->settings.autoDeleteDelay * TIME_SEC)); queue->autoDeleteTask = boost::intrusive_ptr<qpid::sys::TimerTask>(new AutoDeleteTask(broker, queue, connectionId, userId, time)); broker.getClusterTimer().add(queue->autoDeleteTask); QPID_LOG(debug, "Timed auto-delete for " << queue->getName() << " initiated"); @@ -1543,12 +1210,15 @@ void Queue::releaseExclusiveOwnership() { Mutex::ScopedLock locker(ownershipLock); owner = 0; + if (mgmtObject) { + mgmtObject->set_exclusive(false); + } } bool Queue::setExclusiveOwner(const OwnershipToken* const o) { //reset auto deletion timer if necessary - if (autoDeleteTimeout && autoDeleteTask) { + if (settings.autoDeleteDelay && autoDeleteTask) { autoDeleteTask->cancel(); } Mutex::ScopedLock locker(ownershipLock); @@ -1556,6 +1226,9 @@ bool Queue::setExclusiveOwner(const OwnershipToken* const o) return false; } else { owner = o; + if (mgmtObject) { + mgmtObject->set_exclusive(true); + } return true; } } @@ -1687,7 +1360,7 @@ namespace { struct After { framing::SequenceNumber seq; After(framing::SequenceNumber s) : seq(s) {} - bool operator()(const QueuedMessage& qm) { return qm.position > seq; } + bool operator()(const Message& m) { return m.getSequence() > seq; } }; } // namespace @@ -1695,12 +1368,10 @@ struct After { void Queue::setPosition(SequenceNumber n) { Mutex::ScopedLock locker(messageLock); if (n < sequence) { - std::deque<QueuedMessage> dequeued; - dequeueIf(After(n), dequeued); - messages->setPosition(n); + remove(0, After(n), MessagePredicate(), BROWSER); } sequence = n; - QPID_LOG(trace, "Set position to " << sequence << " on " << getName()); + QPID_LOG(debug, "Set position to " << sequence << " on " << getName()); } SequenceNumber Queue::getPosition() { @@ -1721,25 +1392,16 @@ void Queue::recoveryComplete(ExchangeRegistry& exchanges) << "\": exchange does not exist."); } //process any pending dequeues - std::deque<QueuedMessage> pd; - { - Mutex::ScopedLock locker(messageLock); - pendingDequeues.swap(pd); + for (std::vector<Message>::iterator i = pendingDequeues.begin(); i != pendingDequeues.end(); ++i) { + dequeueFromStore(i->getPersistentContext()); } - for_each(pd.begin(), pd.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); -} - -void Queue::insertSequenceNumbers(const std::string& key) -{ - seqNoKey = key; - insertSeqNo = !seqNoKey.empty(); - QPID_LOG(debug, "Inserting sequence numbers as " << key); + pendingDequeues.clear(); } /** 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 qpid::sys::Mutex::ScopedLock&) +void Queue::observeEnqueue(const Message& m, const Mutex::ScopedLock&) { for (Observers::iterator i = observers.begin(); i != observers.end(); ++i) { try { @@ -1748,32 +1410,7 @@ void Queue::observeEnqueue(const QueuedMessage& m, const qpid::sys::Mutex::Scope QPID_LOG(warning, "Exception on notification of enqueue for queue " << getName() << ": " << e.what()); } } -} - -void Queue::updateEnqueued(const QueuedMessage& m) -{ - if (m.payload) { - boost::intrusive_ptr<Message> payload = m.payload; - enqueue(0, payload, true); - { - Mutex::ScopedLock locker(messageLock); - messages->updateAcquired(m); - observeEnqueue(m, locker); - if (policy.get()) { - policy->recoverEnqueued(payload); - policy->enqueued(m); - } - } - mgntEnqStats(m.payload, mgmtObject, brokerMgmtObject); - } else { - QPID_LOG(warning, "Queue informed of enqueued message that has no payload"); - } -} - -bool Queue::isEnqueued(const QueuedMessage& msg) -{ - Mutex::ScopedLock locker(messageLock); - return !policy.get() || policy->isEnqueued(msg); + mgntEnqStats(m, mgmtObject, brokerMgmtObject); } // Note: accessing listeners outside of lock is dangerous. Caller must ensure the queue's @@ -1835,28 +1472,82 @@ void Queue::setDequeueSincePurge(uint32_t value) { dequeueSincePurge = value; } -namespace{ -class FindLowest +void Queue::reject(const QueueCursor& cursor) { - public: - FindLowest() : init(false) {} - void process(const QueuedMessage& message) { - QPID_LOG(debug, "FindLowest processing: " << message.position); - if (!init || message.position < lowest) lowest = message.position; - init = true; - } - bool getLowest(qpid::framing::SequenceNumber& result) { - if (init) { - result = lowest; - return true; + Exchange::shared_ptr alternate = getAlternateExchange(); + Message copy; + boost::intrusive_ptr<PersistableMessage> pmsg; + { + Mutex::ScopedLock locker(messageLock); + Message* message = messages->find(cursor); + if (message) { + if (alternate) copy = *message; + if (message->isPersistent()) pmsg = message->getPersistentContext(); + countRejected(); + observeDequeue(*message, locker); + messages->deleted(cursor); } else { - return false; + return; } } - private: - bool init; - qpid::framing::SequenceNumber lowest; -}; + if (alternate) { + copy.resetDeliveryCount(); + DeliverableMessage delivery(copy, 0); + alternate->routeWithAlternate(delivery); + QPID_LOG(info, "Routed rejected message from " << getName() << " to " + << alternate->getName()); + } else { + //just drop it + QPID_LOG(info, "Dropping rejected message from " << getName()); + } + dequeueFromStore(pmsg); +} + +bool Queue::checkDepth(const QueueDepth& increment, const Message&) +{ + if (current && (settings.maxDepth - current < increment)) { + if (mgmtObject) { + mgmtObject->inc_discardsOverflow(); + if (brokerMgmtObject) + brokerMgmtObject->inc_discardsOverflow(); + } + throw ResourceLimitExceededException(QPID_MSG("Maximum depth exceeded on " << name << ": current=[" << current << "], max=[" << settings.maxDepth << "]")); + } else { + current += increment; + return true; + } +} + +bool Queue::seek(QueueCursor& cursor, MessagePredicate predicate) +{ + Mutex::ScopedLock locker(messageLock); + //hold lock across calls to predicate, or take copy of message? + //currently hold lock, may want to revise depending on any new use + //cases + Message* message = messages->next(cursor); + while (message && (predicate && !predicate(*message))) { + message = messages->next(cursor); + } + return message != 0; +} + +bool Queue::seek(QueueCursor& cursor, MessagePredicate predicate, qpid::framing::SequenceNumber start) +{ + Mutex::ScopedLock locker(messageLock); + //hold lock across calls to predicate, or take copy of message? + //currently hold lock, may want to revise depending on any new use + //cases + Message* message; + message = messages->find(start, &cursor); + if (message && (!predicate || predicate(*message))) return true; + + return seek(cursor, predicate); +} + +bool Queue::seek(QueueCursor& cursor, qpid::framing::SequenceNumber start) +{ + Mutex::ScopedLock locker(messageLock); + return messages->find(start, &cursor); } Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {} diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h index a31e0002ea..671a24d53e 100644 --- a/cpp/src/qpid/broker/Queue.h +++ b/cpp/src/qpid/broker/Queue.h @@ -28,12 +28,14 @@ #include "qpid/broker/Message.h" #include "qpid/broker/Messages.h" #include "qpid/broker/PersistableQueue.h" -#include "qpid/broker/QueuePolicy.h" #include "qpid/broker/QueueBindings.h" #include "qpid/broker/QueueListeners.h" #include "qpid/broker/QueueObserver.h" +#include "qpid/broker/QueueSettings.h" +#include "qpid/broker/TxOp.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/SequenceNumber.h" #include "qpid/sys/AtomicValue.h" #include "qpid/sys/Monitor.h" #include "qpid/sys/Timer.h" @@ -56,10 +58,14 @@ namespace qpid { namespace broker { class Broker; +class Exchange; class MessageStore; +class QueueDepth; class QueueEvents; class QueueRegistry; +class QueueFactory; class TransactionContext; +class TxBuffer; class MessageDistributor; /** @@ -70,7 +76,9 @@ class MessageDistributor; */ class Queue : public boost::enable_shared_from_this<Queue>, public PersistableQueue, public management::Manageable { - + public: + typedef boost::function1<bool, const Message&> MessagePredicate; + protected: struct UsageBarrier { Queue& parent; @@ -90,31 +98,40 @@ class Queue : public boost::enable_shared_from_this<Queue>, ~ScopedUse() { if (acquired) barrier.release(); } }; + class TxPublish : public TxOp + { + Message message; + boost::shared_ptr<Queue> queue; + bool prepared; + public: + TxPublish(const Message&,boost::shared_ptr<Queue>); + bool prepare(TransactionContext* ctxt) throw(); + void commit() throw(); + void rollback() throw(); + }; + typedef std::set< boost::shared_ptr<QueueObserver> > Observers; enum ConsumeCode {NO_MESSAGES=0, CANT_CONSUME=1, CONSUMED=2}; + typedef boost::function1<void, Message&> MessageFunctor; const std::string name; - const bool autodelete; MessageStore* store; const OwnershipToken* owner; 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; bool inLastNodeFailure; - std::string traceId; std::vector<std::string> traceExclude; QueueListeners listeners; std::auto_ptr<Messages> messages; - std::deque<QueuedMessage> pendingDequeues;//used to avoid dequeuing during recovery + std::vector<Message> pendingDequeues; /** 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 @@ -127,9 +144,9 @@ class Queue : public boost::enable_shared_from_this<Queue>, mutable qpid::sys::Monitor messageLock; mutable qpid::sys::Mutex ownershipLock; mutable uint64_t persistenceId; - framing::FieldTable settings; - std::auto_ptr<QueuePolicy> policy; - bool policyExceeded; + const QueueSettings settings; + qpid::framing::FieldTable encodableSettings; + QueueDepth current; QueueBindings bindings; std::string alternateExchangeName; boost::shared_ptr<Exchange> alternateExchange; @@ -139,43 +156,42 @@ class Queue : public boost::enable_shared_from_this<Queue>, sys::AtomicValue<uint32_t> dequeueSincePurge; // Count dequeues since last purge. int eventMode; Observers observers; - bool insertSeqNo; std::string seqNoKey; Broker* broker; bool deleted; UsageBarrier barrier; - int autoDeleteTimeout; boost::intrusive_ptr<qpid::sys::TimerTask> autoDeleteTask; boost::shared_ptr<MessageDistributor> allocator; - void push(boost::intrusive_ptr<Message>& msg, bool isRecovery=false); - void setPolicy(std::auto_ptr<QueuePolicy> policy); - bool getNextMessage(QueuedMessage& msg, Consumer::shared_ptr& c); - ConsumeCode consumeNextMessage(QueuedMessage& msg, Consumer::shared_ptr& c); - bool browseNextMessage(QueuedMessage& msg, Consumer::shared_ptr& c); - void notifyListener(); + virtual void push(Message& msg, bool isRecovery=false); + void process(Message& msg); + bool enqueue(TransactionContext* ctxt, Message& msg); + bool getNextMessage(Message& msg, Consumer::shared_ptr& c); void removeListener(Consumer::shared_ptr); - bool isExcluded(boost::intrusive_ptr<Message>& msg); + bool isExcluded(const Message& msg); - /** 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); + /** update queue observers, stats, policy, etc when the messages' state changes. Lock + * must be held by caller */ + void observeEnqueue(const Message& msg, const sys::Mutex::ScopedLock& lock); + void observeAcquire(const Message& msg, const sys::Mutex::ScopedLock& lock); + void observeRequeue(const Message& msg, const sys::Mutex::ScopedLock& lock); + void observeDequeue(const Message& msg, const sys::Mutex::ScopedLock& lock); 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); + bool acquire(const qpid::framing::SequenceNumber& position, Message& msg, + const qpid::sys::Mutex::ScopedLock& locker); + + void forcePersistent(const Message& msg); int getEventMode(); - void configureImpl(const qpid::framing::FieldTable& settings); - void checkNotDeleted(const Consumer::shared_ptr& c); + void dequeueFromStore(boost::intrusive_ptr<PersistableMessage>); + void abandoned(const Message& message); + void checkNotDeleted(const Consumer::shared_ptr&); void notifyDeleted(); - void dequeueIf(Messages::Predicate predicate, std::deque<QueuedMessage>& dequeued); + uint32_t remove(uint32_t maxCount, MessagePredicate, MessageFunctor, SubscriptionType); + virtual bool checkDepth(const QueueDepth& increment, const Message&); public: @@ -184,12 +200,11 @@ class Queue : public boost::enable_shared_from_this<Queue>, typedef std::vector<shared_ptr> vector; QPID_BROKER_EXTERN Queue(const std::string& name, - bool autodelete = false, + const QueueSettings& settings = QueueSettings(), MessageStore* const store = 0, - const OwnershipToken* const owner = 0, management::Manageable* parent = 0, Broker* broker = 0); - QPID_BROKER_EXTERN ~Queue(); + QPID_BROKER_EXTERN virtual ~Queue(); /** allow the Consumer to consume or browse the next available message */ QPID_BROKER_EXTERN bool dispatch(Consumer::shared_ptr); @@ -198,19 +213,13 @@ class Queue : public boost::enable_shared_from_this<Queue>, * @param msg - message to be acquired. * @return false if message is no longer available for acquire. */ - QPID_BROKER_EXTERN bool acquire(const QueuedMessage& msg, const std::string& consumer); + QPID_BROKER_EXTERN bool acquire(const QueueCursor& msg, const std::string& consumer); /** - * Used to configure a new queue and create a persistent record - * for it in store if required. + * Used to create a persistent record for the queue in store if required. */ - QPID_BROKER_EXTERN void create(const qpid::framing::FieldTable& settings); + QPID_BROKER_EXTERN void create(); - /** - * Used to reconfigure a recovered queue (does not create - * persistent record in store). - */ - QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings); void destroyed(); QPID_BROKER_EXTERN void bound(const std::string& exchange, const std::string& key, @@ -224,34 +233,36 @@ class Queue : public boost::enable_shared_from_this<Queue>, 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. - * @param message - set to the acquired message if true returned. - * @return true if the message has been acquired. + /** + * Removes (and dequeues) a message by its sequence number (used + * for some broker features, e.g. queue replication) + * + * @param position the sequence number of the message to be dequeued. + * @return true if the message is dequeued. */ - QPID_BROKER_EXTERN bool acquireMessageAt(const qpid::framing::SequenceNumber& position, QueuedMessage& message); + QPID_BROKER_EXTERN bool dequeueMessageAt(const qpid::framing::SequenceNumber& position); /** * Delivers a message to the queue. Will record it as * enqueued if persistent then process it. */ - QPID_BROKER_EXTERN void deliver(boost::intrusive_ptr<Message> msg); - /** - * Dispatches the messages immediately to a consumer if - * one is available or stores it for later if not. - */ - QPID_BROKER_EXTERN void process(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void deliver(Message, TxBuffer* = 0); /** * Returns a message to the in-memory queue (due to lack * of acknowledegement from a receiver). If a consumer is * available it will be dispatched immediately, else it * will be returned to the front of the queue. */ - QPID_BROKER_EXTERN void requeue(const QueuedMessage& msg); + QPID_BROKER_EXTERN void release(const QueueCursor& msg, bool markRedelivered=true); + QPID_BROKER_EXTERN void reject(const QueueCursor& msg); + + QPID_BROKER_EXTERN bool seek(QueueCursor&, MessagePredicate); + QPID_BROKER_EXTERN bool seek(QueueCursor&, MessagePredicate, qpid::framing::SequenceNumber start); + QPID_BROKER_EXTERN bool seek(QueueCursor&, qpid::framing::SequenceNumber start); /** * Used during recovery to add stored messages back to the queue */ - QPID_BROKER_EXTERN void recover(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void recover(Message& msg); QPID_BROKER_EXTERN void consume(Consumer::shared_ptr c, bool exclusive = false); @@ -268,7 +279,6 @@ class Queue : public boost::enable_shared_from_this<Queue>, 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; } QPID_BROKER_EXTERN bool isExclusiveOwner(const OwnershipToken* const o) const; @@ -277,8 +287,9 @@ class Queue : public boost::enable_shared_from_this<Queue>, 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; } + inline const QueueSettings& getSettings() const { return settings; } + inline const qpid::framing::FieldTable& getEncodableSettings() const { return encodableSettings; } + inline bool isAutoDelete() const { return settings.autodelete; } QPID_BROKER_EXTERN bool canAutoDelete() const; const QueueBindings& getBindings() const { return bindings; } @@ -288,48 +299,22 @@ class Queue : public boost::enable_shared_from_this<Queue>, QPID_BROKER_EXTERN void setLastNodeFailure(); QPID_BROKER_EXTERN void clearLastNodeFailure(); - 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) */ - QPID_BROKER_EXTERN bool dequeue(TransactionContext* ctxt, const QueuedMessage &msg); + QPID_BROKER_EXTERN void dequeue(TransactionContext* ctxt, const QueueCursor&); /** * Inform the queue that a previous transactional dequeue * committed. */ - QPID_BROKER_EXTERN void dequeueCommitted(const QueuedMessage& msg); - - /** - * Inform queue of messages that were enqueued, have since - * been acquired but not yet accepted or released (and - * thus are still logically on the queue) - used in - * clustered broker. - */ - QPID_BROKER_EXTERN void updateEnqueued(const QueuedMessage& msg); - - /** - * Test whether the specified message (identified by its - * sequence/position), is still enqueued (note this - * doesn't mean it is available for delivery as it may - * have been delievered to a subscriber who has not yet - * accepted it). - */ - QPID_BROKER_EXTERN bool isEnqueued(const QueuedMessage& msg); - - /** - * Acquires the next available (oldest) message - */ - QPID_BROKER_EXTERN QueuedMessage get(); + void dequeueCommitted(const QueueCursor& msg); /** Get the message at position pos, returns true if found and sets msg */ - QPID_BROKER_EXTERN bool find(framing::SequenceNumber pos, QueuedMessage& msg ) const; - - QPID_BROKER_EXTERN const QueuePolicy* getPolicy(); + QPID_BROKER_EXTERN bool find(framing::SequenceNumber pos, Message& msg ) const; 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); + QPID_BROKER_EXTERN bool isLocal(const Message& msg); //PersistableQueue support: QPID_BROKER_EXTERN uint64_t getPersistenceId() const; @@ -410,7 +395,11 @@ class Queue : public boost::enable_shared_from_this<Queue>, * Reserve space in policy for an enqueued message that * has been recovered in the prepared state (dtx only) */ - QPID_BROKER_EXTERN void recoverPrepared(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void recoverPrepared(const Message& msg); + void enqueueAborted(const Message& msg); + void enqueueCommited(Message& msg); + void dequeueAborted(Message& msg); + void dequeueCommited(const Message& msg); QPID_BROKER_EXTERN void flush(); @@ -418,6 +407,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, uint32_t getDequeueSincePurge() { return dequeueSincePurge.get(); } QPID_BROKER_EXTERN void setDequeueSincePurge(uint32_t value); + friend class QueueFactory; }; } } diff --git a/cpp/src/qpid/broker/QueueAsyncContext.cpp b/cpp/src/qpid/broker/QueueAsyncContext.cpp index 02eb2e9546..1bad5387a3 100644 --- a/cpp/src/qpid/broker/QueueAsyncContext.cpp +++ b/cpp/src/qpid/broker/QueueAsyncContext.cpp @@ -37,6 +37,7 @@ QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, m_arq(arq) {} +/* QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, boost::intrusive_ptr<PersistableMessage> msg, AsyncResultCallback rcb, @@ -46,6 +47,7 @@ QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, m_rcb(rcb), m_arq(arq) {} +*/ QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, SimpleTxnBuffer* tb, @@ -59,6 +61,7 @@ QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, assert(m_q.get() != 0); } +/* QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, boost::intrusive_ptr<PersistableMessage> msg, SimpleTxnBuffer* tb, @@ -73,6 +76,7 @@ QueueAsyncContext::QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, assert(m_q.get() != 0); assert(m_msg.get() != 0); } +*/ QueueAsyncContext::~QueueAsyncContext() {} @@ -83,11 +87,13 @@ QueueAsyncContext::getQueue() const return m_q; } +/* boost::intrusive_ptr<PersistableMessage> QueueAsyncContext::getMessage() const { return m_msg; } +*/ SimpleTxnBuffer* QueueAsyncContext::getTxnBuffer() const { diff --git a/cpp/src/qpid/broker/QueueAsyncContext.h b/cpp/src/qpid/broker/QueueAsyncContext.h index 8657922377..7a159f2639 100644 --- a/cpp/src/qpid/broker/QueueAsyncContext.h +++ b/cpp/src/qpid/broker/QueueAsyncContext.h @@ -47,22 +47,22 @@ public: QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, AsyncResultCallback rcb, AsyncResultQueue* const arq); - QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, +/* QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, boost::intrusive_ptr<PersistableMessage> msg, AsyncResultCallback rcb, - AsyncResultQueue* const arq); + AsyncResultQueue* const arq);*/ QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, SimpleTxnBuffer* tb, AsyncResultCallback rcb, AsyncResultQueue* const arq); - QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, +/* QueueAsyncContext(boost::shared_ptr<PersistableQueue> q, boost::intrusive_ptr<PersistableMessage> msg, SimpleTxnBuffer* tb, AsyncResultCallback rcb, - AsyncResultQueue* const arq); + AsyncResultQueue* const arq);*/ virtual ~QueueAsyncContext(); boost::shared_ptr<PersistableQueue> getQueue() const; - boost::intrusive_ptr<PersistableMessage> getMessage() const; +// boost::intrusive_ptr<PersistableMessage> getMessage() const; SimpleTxnBuffer* getTxnBuffer() const; AsyncResultQueue* getAsyncResultQueue() const; AsyncResultCallback getAsyncResultCallback() const; @@ -71,7 +71,7 @@ public: private: boost::shared_ptr<PersistableQueue> m_q; - boost::intrusive_ptr<PersistableMessage> m_msg; +// boost::intrusive_ptr<PersistableMessage> m_msg; SimpleTxnBuffer* m_tb; AsyncResultCallback m_rcb; AsyncResultQueue* const m_arq; diff --git a/cpp/src/qpid/cluster/ExpiryPolicy.cpp b/cpp/src/qpid/broker/QueueCursor.cpp index 0ef5c2a35d..e48b18b748 100644 --- a/cpp/src/qpid/cluster/ExpiryPolicy.cpp +++ b/cpp/src/qpid/broker/QueueCursor.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,24 +18,27 @@ * under the License. * */ - +#include "QueueCursor.h" #include "qpid/broker/Message.h" -#include "qpid/cluster/ExpiryPolicy.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/sys/Time.h" -#include "qpid/log/Statement.h" namespace qpid { -namespace cluster { +namespace broker { +QueueCursor::QueueCursor(SubscriptionType t) : type(t), position(0), version(0), valid(false) {} -ExpiryPolicy::ExpiryPolicy(Cluster& cluster) : cluster(cluster) {} - -bool ExpiryPolicy::hasExpired(broker::Message& m) { - return m.getExpiration() < cluster.getClusterTime(); +void QueueCursor::setPosition(int32_t p, int32_t v) +{ + position = p; + version = v; + valid = true; } -sys::AbsTime ExpiryPolicy::getCurrentTime() { - return cluster.getClusterTime(); +bool QueueCursor::check(const Message& m) +{ + return (m.getState() == AVAILABLE || ((type == REPLICATOR || type == PURGE) && m.getState() == ACQUIRED)); } -}} // namespace qpid::cluster +bool QueueCursor::isValid(int32_t v) +{ + return valid && (valid = (v == version)); +} +}} // namespace qpid::broker diff --git a/cpp/src/qpid/cluster/SecureConnectionFactory.h b/cpp/src/qpid/broker/QueueCursor.h index 24d1fcfee5..2551b64a48 100644 --- a/cpp/src/qpid/cluster/SecureConnectionFactory.h +++ b/cpp/src/qpid/broker/QueueCursor.h @@ -1,3 +1,6 @@ +#ifndef QPID_BROKER_QUEUECURSOR_H +#define QPID_BROKER_QUEUECURSOR_H + /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -7,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,41 +21,51 @@ * under the License. * */ -#ifndef QPID_CLUSTER_SecureconnectionFactory -#define QPID_CLUSTER_SecureconnectionFactory - -#include "qpid/sys/ConnectionCodec.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/sys/IntegerTypes.h" #include <boost/shared_ptr.hpp> namespace qpid { - namespace broker { - class Broker; -} -namespace cluster { +class Message; + +enum SubscriptionType +{ + CONSUMER, + BROWSER, + PURGE, + REPLICATOR +}; -class SecureConnectionFactory : public qpid::sys::ConnectionCodec::Factory +class CursorContext { + public: + virtual ~CursorContext() {} +}; +/** + * + */ +class QueueCursor { public: - typedef boost::shared_ptr<qpid::sys::ConnectionCodec::Factory> CodecFactoryPtr; - SecureConnectionFactory(CodecFactoryPtr f); - - qpid::sys::ConnectionCodec* create( - framing::ProtocolVersion, qpid::sys::OutputControl&, const std::string& id, - const qpid::sys::SecuritySettings& - ); - - /** Return "preferred" codec for outbound connections. */ - qpid::sys::ConnectionCodec* create( - qpid::sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings& - ); + QPID_BROKER_EXTERN QueueCursor(SubscriptionType type = CONSUMER); private: - CodecFactoryPtr codecFactory; -}; + SubscriptionType type; + int32_t position; + int32_t version; + bool valid; + boost::shared_ptr<CursorContext> context; -}} // namespace qpid::cluster + void setPosition(int32_t p, int32_t v); + bool check(const Message& m); + bool isValid(int32_t v); + friend class MessageDeque; + friend class MessageMap; + friend class PriorityQueue; + template <typename T> friend class IndexedDeque; +}; +}} // namespace qpid::broker -#endif // QPID_CLUSTER_SecureconnectionFactory +#endif /*!QPID_BROKER_QUEUECURSOR_H*/ diff --git a/cpp/src/qpid/broker/QueueDepth.cpp b/cpp/src/qpid/broker/QueueDepth.cpp new file mode 100644 index 0000000000..69ec0ab4ac --- /dev/null +++ b/cpp/src/qpid/broker/QueueDepth.cpp @@ -0,0 +1,127 @@ +/* + * + * 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 "QueueDepth.h" + +namespace qpid { +namespace broker { + +QueueDepth::QueueDepth() {} +QueueDepth::QueueDepth(uint32_t c, uint64_t s) : count(c), size(s) {} +QueueDepth& QueueDepth::operator+=(const QueueDepth& other) +{ + if (count.valid) count.value += other.count.value; + if (size.valid) size.value += other.size.value; + return *this; +} +QueueDepth& QueueDepth::operator-=(const QueueDepth& other) +{ + if (count.valid) count.value -= other.count.value; + if (size.valid) size.value -= other.size.value; + return *this; +} +bool QueueDepth::operator==(const QueueDepth& other) const +{ + //only compare values, not validity an invalid value is always 0; + //this means that an invalid value will match an empty queue + //depth, which is fine + return (count.value == other.count.value) + && (size.value == other.size.value); +} +bool QueueDepth::operator!=(const QueueDepth& other) const +{ + return !(*this == other); +} +bool QueueDepth::operator<(const QueueDepth& other) const +{ + if (count.valid && size.valid) + return count.value < other.count.value || size.value < other.size.value; + else if (count.valid) + return count.value < other.count.value; + else + return size.value < other.size.value; +} +bool QueueDepth::operator>(const QueueDepth& other) const +{ + if (count.valid && size.valid) + return count.value > other.count.value || size.value > other.size.value; + else if (count.valid) + return count.value > other.count.value; + else + return size.value > other.size.value; +} +bool QueueDepth::hasCount() const { return count.valid; } +uint32_t QueueDepth::getCount() const { return count.value; } +void QueueDepth::setCount(uint32_t c) { count.value = c; count.valid = true; } +bool QueueDepth::hasSize() const { return size.valid; } +uint64_t QueueDepth::getSize() const { return size.value; } +void QueueDepth::setSize(uint64_t c) { size.value = c; size.valid = true; } + +namespace{ + template <typename T> QueueDepth::Optional<T> add(const QueueDepth::Optional<T>& a, const QueueDepth::Optional<T>& b) + { + QueueDepth::Optional<T> result; + if (a.valid && b.valid) { + result.valid = true; + result.value = a.value + b.value; + } + return result; + } + template <typename T> QueueDepth::Optional<T> subtract(const QueueDepth::Optional<T>& a, const QueueDepth::Optional<T>& b) + { + QueueDepth::Optional<T> result; + if (a.valid && b.valid) { + result.valid = true; + result.value = a.value - b.value; + } + return result; + } +} +QueueDepth operator-(const QueueDepth& a, const QueueDepth& b) +{ + QueueDepth result; + result.count = subtract(a.count, b.count); + result.size = subtract(a.size, b.size); + return result; +} + +QueueDepth operator+(const QueueDepth& a, const QueueDepth& b) +{ + QueueDepth result; + result.count = add(a.count, b.count); + result.size = add(a.size, b.size); + return result; + +} + +std::ostream& operator<<(std::ostream& o, const QueueDepth& d) +{ + if (d.hasCount()) o << "count: " << d.getCount(); + if (d.hasSize()) { + if (d.hasCount()) o << ", "; + o << "size: " << d.getSize(); + } + return o; +} + +QueueDepth::operator bool() const { return hasCount() || hasSize(); } + + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueDepth.h b/cpp/src/qpid/broker/QueueDepth.h new file mode 100644 index 0000000000..d93acb2a7a --- /dev/null +++ b/cpp/src/qpid/broker/QueueDepth.h @@ -0,0 +1,74 @@ +#ifndef QPID_BROKER_QUEUEDEPTH_H +#define QPID_BROKER_QUEUEDEPTH_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/BrokerImportExport.h" +#include "qpid/sys/IntegerTypes.h" +#include <ostream> + +namespace qpid { +namespace broker { + +/** + * Represents a queue depth in message count and/or aggregate message + * size. + */ +class QueueDepth +{ + public: + QPID_BROKER_EXTERN QueueDepth(); + QPID_BROKER_EXTERN QueueDepth(uint32_t count, uint64_t size); + QPID_BROKER_EXTERN QueueDepth& operator+=(const QueueDepth&); + QPID_BROKER_EXTERN QueueDepth& operator-=(const QueueDepth&); + QPID_BROKER_EXTERN bool operator==(const QueueDepth&) const; + QPID_BROKER_EXTERN bool operator!=(const QueueDepth&) const; + QPID_BROKER_EXTERN bool operator<(const QueueDepth& other) const; + QPID_BROKER_EXTERN bool operator>(const QueueDepth& other) const; + QPID_BROKER_EXTERN operator bool() const; + QPID_BROKER_EXTERN bool hasCount() const; + QPID_BROKER_EXTERN uint32_t getCount() const; + QPID_BROKER_EXTERN void setCount(uint32_t); + QPID_BROKER_EXTERN bool hasSize() const; + QPID_BROKER_EXTERN uint64_t getSize() const; + QPID_BROKER_EXTERN void setSize(uint64_t); + friend QPID_BROKER_EXTERN QueueDepth operator-(const QueueDepth&, const QueueDepth&); + friend QPID_BROKER_EXTERN QueueDepth operator+(const QueueDepth&, const QueueDepth&); + template <typename T> struct Optional + { + T value; + bool valid; + + Optional(T v) : value(v), valid(true) {} + Optional() : value(0), valid(false) {} + }; + private: + Optional<uint32_t> count; + Optional<uint64_t> size; +}; + +QPID_BROKER_EXTERN QueueDepth operator-(const QueueDepth&, const QueueDepth&); +QPID_BROKER_EXTERN QueueDepth operator+(const QueueDepth&, const QueueDepth&); +std::ostream& operator<<(std::ostream&, const QueueDepth&); + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUEDEPTH_H*/ diff --git a/cpp/src/qpid/broker/QueueEvents.cpp b/cpp/src/qpid/broker/QueueEvents.cpp deleted file mode 100644 index b102a8554d..0000000000 --- a/cpp/src/qpid/broker/QueueEvents.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/broker/QueueEvents.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/QueueObserver.h" -#include "qpid/Exception.h" -#include "qpid/log/Statement.h" - -namespace qpid { -namespace broker { - -QueueEvents::QueueEvents(const boost::shared_ptr<sys::Poller>& poller, bool isSync) : - eventQueue(boost::bind(&QueueEvents::handle, this, _1), poller), enabled(true), sync(isSync) -{ - if (!sync) eventQueue.start(); -} - -QueueEvents::~QueueEvents() -{ - if (!sync) eventQueue.stop(); -} - -void QueueEvents::enqueued(const QueuedMessage& m) -{ - if (enabled) { - Event enq(ENQUEUE, m); - if (sync) { - for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) - j->second(enq); - } else { - eventQueue.push(enq); - } - } -} - -void QueueEvents::dequeued(const QueuedMessage& m) -{ - if (enabled) { - Event deq(DEQUEUE, m); - if (sync) { - for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) - j->second(deq); - } else { - eventQueue.push(Event(DEQUEUE, m)); - } - } -} - -void QueueEvents::registerListener(const std::string& id, const EventListener& listener) -{ - qpid::sys::Mutex::ScopedLock l(lock); - if (listeners.find(id) == listeners.end()) { - listeners[id] = listener; - } else { - throw Exception(QPID_MSG("Event listener already registered for '" << id << "'")); - } -} - -void QueueEvents::unregisterListener(const std::string& id) -{ - qpid::sys::Mutex::ScopedLock l(lock); - if (listeners.find(id) == listeners.end()) { - throw Exception(QPID_MSG("No event listener registered for '" << id << "'")); - } else { - listeners.erase(id); - } -} - -QueueEvents::EventQueue::Batch::const_iterator -QueueEvents::handle(const EventQueue::Batch& events) { - qpid::sys::Mutex::ScopedLock l(lock); - for (EventQueue::Batch::const_iterator i = events.begin(); i != events.end(); ++i) { - for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) { - j->second(*i); - } - } - return events.end(); -} - -void QueueEvents::shutdown() -{ - if (!sync && !eventQueue.empty() && !listeners.empty()) eventQueue.shutdown(); -} - -void QueueEvents::enable() -{ - enabled = true; - QPID_LOG(debug, "Queue events enabled"); -} - -void QueueEvents::disable() -{ - enabled = false; - QPID_LOG(debug, "Queue events disabled"); -} - -bool QueueEvents::isSync() -{ - return sync; -} - -class EventGenerator : public QueueObserver -{ - public: - EventGenerator(QueueEvents& mgr, bool enqOnly) : manager(mgr), enqueueOnly(enqOnly) {} - void enqueued(const QueuedMessage& m) - { - manager.enqueued(m); - } - void dequeued(const QueuedMessage& m) - { - if (!enqueueOnly) manager.dequeued(m); - } - - void acquired(const QueuedMessage&) {}; - void requeued(const QueuedMessage&) {}; - - private: - QueueEvents& manager; - const bool enqueueOnly; -}; - -void QueueEvents::observe(Queue& queue, bool enqueueOnly) -{ - boost::shared_ptr<QueueObserver> observer(new EventGenerator(*this, enqueueOnly)); - queue.addObserver(observer); -} - - -QueueEvents::Event::Event(EventType t, const QueuedMessage& m) : type(t), msg(m) {} - - -}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueEvents.h b/cpp/src/qpid/broker/QueueEvents.h deleted file mode 100644 index d0a267e6e2..0000000000 --- a/cpp/src/qpid/broker/QueueEvents.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef QPID_BROKER_QUEUEEVENTS_H -#define QPID_BROKER_QUEUEEVENTS_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/BrokerImportExport.h" -#include "qpid/broker/QueuedMessage.h" -#include "qpid/sys/Mutex.h" -#include "qpid/sys/PollableQueue.h" -#include <map> -#include <string> -#include <boost/function.hpp> - -namespace qpid { -namespace broker { - -/** - * Event manager for queue events. Allows queues to indicate when - * events have occurred; allows listeners to register for notification - * of this. The notification happens asynchronously, in a separate - * thread. - */ -class QueueEvents -{ - public: - enum EventType {ENQUEUE, DEQUEUE}; - - struct Event - { - EventType type; - QueuedMessage msg; - - QPID_BROKER_EXTERN Event(EventType, const QueuedMessage&); - }; - - typedef boost::function<void (Event)> EventListener; - - QPID_BROKER_EXTERN QueueEvents(const boost::shared_ptr<sys::Poller>& poller, bool isSync = false); - QPID_BROKER_EXTERN ~QueueEvents(); - QPID_BROKER_EXTERN void enqueued(const QueuedMessage&); - QPID_BROKER_EXTERN void dequeued(const QueuedMessage&); - QPID_BROKER_EXTERN void registerListener(const std::string& id, - const EventListener&); - QPID_BROKER_EXTERN void unregisterListener(const std::string& id); - void enable(); - void disable(); - void observe(Queue&, bool enqueueOnly); - //process all outstanding events - QPID_BROKER_EXTERN void shutdown(); - QPID_BROKER_EXTERN bool isSync(); - private: - typedef qpid::sys::PollableQueue<Event> EventQueue; - typedef std::map<std::string, EventListener> Listeners; - - EventQueue eventQueue; - Listeners listeners; - volatile bool enabled; - qpid::sys::Mutex lock;//protect listeners from concurrent access - bool sync; - - EventQueue::Batch::const_iterator handle(const EventQueue::Batch& e); - -}; -}} // namespace qpid::broker - -#endif /*!QPID_BROKER_QUEUEEVENTS_H*/ diff --git a/cpp/src/qpid/broker/QueueFactory.cpp b/cpp/src/qpid/broker/QueueFactory.cpp new file mode 100644 index 0000000000..efeb9ae53b --- /dev/null +++ b/cpp/src/qpid/broker/QueueFactory.cpp @@ -0,0 +1,114 @@ +/* + * + * 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/QueueFactory.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/QueueSettings.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/LossyQueue.h" +#include "qpid/broker/Lvq.h" +#include "qpid/broker/Messages.h" +#include "qpid/broker/MessageDistributor.h" +#include "qpid/broker/MessageGroupManager.h" +#include "qpid/broker/Fairshare.h" +#include "qpid/broker/MessageDeque.h" +#include "qpid/broker/MessageMap.h" +#include "qpid/broker/PriorityQueue.h" +#include "qpid/broker/QueueFlowLimit.h" +#include "qpid/broker/ThresholdAlerts.h" +#include "qpid/broker/FifoDistributor.h" +#include <map> +#include <memory> + +namespace qpid { +namespace broker { + + +QueueFactory::QueueFactory() : broker(0), store(0), parent(0) {} + +boost::shared_ptr<Queue> QueueFactory::create(const std::string& name, const QueueSettings& settings) +{ + settings.validate(); + + //1. determine Queue type (i.e. whether we are subclassing Queue) + // -> if 'ring' policy is in use then subclass + boost::shared_ptr<Queue> queue; + if (settings.dropMessagesAtLimit) { + queue = boost::shared_ptr<Queue>(new LossyQueue(name, settings, settings.durable ? store : 0, parent, broker)); + } else if (settings.lvqKey.size()) { + std::auto_ptr<MessageMap> map(new MessageMap(settings.lvqKey)); + queue = boost::shared_ptr<Queue>(new Lvq(name, map, settings, settings.durable ? store : 0, parent, broker)); + } else { + queue = boost::shared_ptr<Queue>(new Queue(name, settings, settings.durable ? store : 0, parent, broker)); + } + + //2. determine Messages type (i.e. structure) + if (settings.priorities) { + if (settings.defaultFairshare || settings.fairshare.size()) { + queue->messages = Fairshare::create(settings); + } else { + queue->messages = std::auto_ptr<Messages>(new PriorityQueue(settings.priorities)); + } + } else if (settings.lvqKey.empty()) {//LVQ already handled above + queue->messages = std::auto_ptr<Messages>(new MessageDeque()); + } + + //3. determine MessageDistributor type + if (settings.groupKey.size()) { + boost::shared_ptr<MessageGroupManager> mgm(MessageGroupManager::create( name, *(queue->messages), settings)); + queue->allocator = mgm; + queue->addObserver(mgm); + } else { + queue->allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *(queue->messages) )); + } + + + //4. threshold event config + if (broker && broker->getManagementAgent()) { + ThresholdAlerts::observe(*queue, *(broker->getManagementAgent()), settings, broker->getOptions().queueThresholdEventRatio); + } + //5. flow control config + QueueFlowLimit::observe(*queue, settings); + + return queue; +} + +void QueueFactory::setBroker(Broker* b) +{ + broker = b; +} +Broker* QueueFactory::getBroker() +{ + return broker; +} +void QueueFactory::setStore (MessageStore* s) +{ + store = s; +} +MessageStore* QueueFactory::getStore() const +{ + return store; +} +void QueueFactory::setParent(management::Manageable* p) +{ + parent = p; +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueFactory.h b/cpp/src/qpid/broker/QueueFactory.h new file mode 100644 index 0000000000..b6a79f1f1a --- /dev/null +++ b/cpp/src/qpid/broker/QueueFactory.h @@ -0,0 +1,73 @@ +#ifndef QPID_BROKER_QUEUEFACTORY_H +#define QPID_BROKER_QUEUEFACTORY_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/BrokerImportExport.h" +#include "qpid/types/Variant.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace management { +class Manageable; +} +namespace broker { +class Broker; +class MessageStore; +class Queue; +struct QueueSettings; + +/** + * Handles the creation and configuration of a Queue instance in order + * to meet the required settings + */ +class QueueFactory +{ + public: + QPID_BROKER_EXTERN QueueFactory(); + + QPID_BROKER_EXTERN boost::shared_ptr<Queue> create(const std::string& name, const QueueSettings& settings); + + void setBroker(Broker*); + Broker* getBroker(); + + /** + * Set the store to use. May only be called once. + */ + void setStore (MessageStore*); + + /** + * Return the message store used. + */ + MessageStore* getStore() const; + + /** + * Register the manageable parent for declared queues + */ + void setParent(management::Manageable*); + private: + Broker* broker; + MessageStore* store; + management::Manageable* parent; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUEFACTORY_H*/ diff --git a/cpp/src/qpid/broker/QueueFlowLimit.cpp b/cpp/src/qpid/broker/QueueFlowLimit.cpp index 14fe5f4022..11b9cbae63 100644 --- a/cpp/src/qpid/broker/QueueFlowLimit.cpp +++ b/cpp/src/qpid/broker/QueueFlowLimit.cpp @@ -20,7 +20,9 @@ */ #include "qpid/broker/QueueFlowLimit.h" #include "qpid/broker/Broker.h" +#include "qpid/broker/Message.h" #include "qpid/broker/Queue.h" +#include "qpid/broker/QueueSettings.h" #include "qpid/Exception.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/reply_exceptions.h" @@ -57,34 +59,6 @@ namespace { << "=" << max)); } } - - /** extract a capacity value as passed in an argument map - */ - uint64_t getCapacity(const FieldTable& settings, const std::string& key, uint64_t defaultValue) - { - FieldTable::ValuePtr v = settings.get(key); - - int64_t result = 0; - - if (!v) return defaultValue; - if (v->getType() == 0x23) { - QPID_LOG(debug, "Value for " << key << " specified as float: " << v->get<float>()); - } else if (v->getType() == 0x33) { - QPID_LOG(debug, "Value for " << key << " specified as double: " << v->get<double>()); - } else if (v->convertsTo<int64_t>()) { - result = v->get<int64_t>(); - QPID_LOG(debug, "Got integer value for " << key << ": " << result); - if (result >= 0) return result; - } else if (v->convertsTo<std::string>()) { - std::string s(v->get<std::string>()); - QPID_LOG(debug, "Got string value for " << key << ": " << s); - std::istringstream convert(s); - if (convert >> result && result >= 0) return result; - } - - QPID_LOG(warning, "Cannot convert " << key << " to unsigned integer, using default (" << defaultValue << ")"); - return defaultValue; - } } @@ -102,10 +76,8 @@ QueueFlowLimit::QueueFlowLimit(Queue *_queue, if (queue) { queueName = _queue->getName(); - if (queue->getPolicy()) { - maxSize = _queue->getPolicy()->getMaxSize(); - maxCount = _queue->getPolicy()->getMaxCount(); - } + if (queue->getSettings().maxDepth.hasCount()) maxCount = queue->getSettings().maxDepth.getCount(); + if (queue->getSettings().maxDepth.hasCount()) maxSize = queue->getSettings().maxDepth.getSize(); broker = queue->getBroker(); queueMgmtObj = dynamic_cast<_qmfBroker::Queue*> (queue->GetManagementObject()); if (queueMgmtObj) { @@ -125,23 +97,23 @@ QueueFlowLimit::~QueueFlowLimit() sys::Mutex::ScopedLock l(indexLock); if (!index.empty()) { // we're gone - release all pending msgs - for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.begin(); + for (std::map<framing::SequenceNumber, Message >::iterator itr = index.begin(); itr != index.end(); ++itr) if (itr->second) try { - itr->second->getIngressCompletion().finishCompleter(); + itr->second.getPersistentContext()->getIngressCompletion().finishCompleter(); } catch (...) {} // ignore - not safe for a destructor to throw. index.clear(); } } -void QueueFlowLimit::enqueued(const QueuedMessage& msg) +void QueueFlowLimit::enqueued(const Message& msg) { sys::Mutex::ScopedLock l(indexLock); ++count; - size += msg.payload->contentSize(); + size += msg.getContentSize(); if (!flowStopped) { if (flowStopCount && count > flowStopCount) { @@ -160,13 +132,13 @@ void QueueFlowLimit::enqueued(const QueuedMessage& msg) if (flowStopped || !index.empty()) { // ignore flow control if we are populating the queue due to cluster replication: if (broker && broker->isClusterUpdatee()) { - QPID_LOG(trace, "Queue \"" << queueName << "\": ignoring flow control for msg pos=" << msg.position); + QPID_LOG(trace, "Queue \"" << queueName << "\": ignoring flow control for msg pos=" << msg.getSequence()); return; } - QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.position); - msg.payload->getIngressCompletion().startCompleter(); // don't complete until flow resumes + QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.getSequence()); + msg.getPersistentContext()->getIngressCompletion().startCompleter(); // don't complete until flow resumes bool unique; - unique = index.insert(std::pair<framing::SequenceNumber, boost::intrusive_ptr<Message> >(msg.position, msg.payload)).second; + unique = index.insert(std::pair<framing::SequenceNumber, Message >(msg.getSequence(), msg)).second; // Like this to avoid tripping up unused variable warning when NDEBUG set if (!unique) assert(unique); } @@ -174,7 +146,7 @@ void QueueFlowLimit::enqueued(const QueuedMessage& msg) -void QueueFlowLimit::dequeued(const QueuedMessage& msg) +void QueueFlowLimit::dequeued(const Message& msg) { sys::Mutex::ScopedLock l(indexLock); @@ -184,7 +156,7 @@ void QueueFlowLimit::dequeued(const QueuedMessage& msg) throw Exception(QPID_MSG("Flow limit count underflow on dequeue. Queue=" << queueName)); } - uint64_t _size = msg.payload->contentSize(); + uint64_t _size = msg.getContentSize(); if (_size <= size) { size -= _size; } else { @@ -203,16 +175,16 @@ void QueueFlowLimit::dequeued(const QueuedMessage& msg) if (!index.empty()) { if (!flowStopped) { // flow enabled - release all pending msgs - for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.begin(); + for (std::map<framing::SequenceNumber, Message >::iterator itr = index.begin(); itr != index.end(); ++itr) if (itr->second) - itr->second->getIngressCompletion().finishCompleter(); + itr->second.getPersistentContext()->getIngressCompletion().finishCompleter(); index.clear(); } else { // even if flow controlled, we must release this msg as it is being dequeued - std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.find(msg.position); + std::map<framing::SequenceNumber, Message >::iterator itr = index.find(msg.getSequence()); if (itr != index.end()) { // this msg is flow controlled, release it: - msg.payload->getIngressCompletion().finishCompleter(); + msg.getPersistentContext()->getIngressCompletion().finishCompleter(); index.erase(itr); } } @@ -279,7 +251,7 @@ void QueueFlowLimit::setDefaults(uint64_t maxQueueSize, uint flowStopRatio, uint } -void QueueFlowLimit::observe(Queue& queue, const qpid::framing::FieldTable& settings) +void QueueFlowLimit::observe(Queue& queue, const QueueSettings& settings) { QueueFlowLimit *ptr = createLimit( &queue, settings ); if (ptr) { @@ -289,36 +261,37 @@ void QueueFlowLimit::observe(Queue& queue, const qpid::framing::FieldTable& sett } /** returns ptr to a QueueFlowLimit, else 0 if no limit */ -QueueFlowLimit *QueueFlowLimit::createLimit(Queue *queue, const qpid::framing::FieldTable& settings) +QueueFlowLimit *QueueFlowLimit::createLimit(Queue *queue, const QueueSettings& settings) { - std::string type(QueuePolicy::getType(settings)); - - if (type == QueuePolicy::RING || type == QueuePolicy::RING_STRICT) { + if (settings.dropMessagesAtLimit) { // The size of a RING queue is limited by design - no need for flow control. return 0; } - if (settings.get(flowStopCountKey) || settings.get(flowStopSizeKey) || - settings.get(flowResumeCountKey) || settings.get(flowResumeSizeKey)) { + if (settings.flowStop.hasCount() || settings.flowStop.hasSize()) { // user provided (some) flow settings manually... - uint32_t flowStopCount = getCapacity(settings, flowStopCountKey, 0); - uint32_t flowResumeCount = getCapacity(settings, flowResumeCountKey, 0); - uint64_t flowStopSize = getCapacity(settings, flowStopSizeKey, 0); - uint64_t flowResumeSize = getCapacity(settings, flowResumeSizeKey, 0); - if (flowStopCount == 0 && flowStopSize == 0) { // disable flow control + if (settings.flowStop.getCount() || settings.flowStop.getSize()) { + return new QueueFlowLimit(queue, + settings.flowStop.getCount(), + settings.flowResume.getCount(), + settings.flowStop.getSize(), + settings.flowResume.getSize()); + } else { + //don't have a non-zero value for either the count or the + //size to stop at, yet at least one of these settings was + //provided, i.e it was set to 0 explicitly which we treat + //as turning it off return 0; } - return new QueueFlowLimit(queue, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize); } if (defaultFlowStopRatio) { // broker has a default ratio setup... - uint64_t maxByteCount = getCapacity(settings, QueuePolicy::maxSizeKey, defaultMaxSize); + uint64_t maxByteCount = settings.maxDepth.hasSize() ? settings.maxDepth.getSize() : defaultMaxSize; uint64_t flowStopSize = (uint64_t)(maxByteCount * (defaultFlowStopRatio/100.0) + 0.5); uint64_t flowResumeSize = (uint64_t)(maxByteCount * (defaultFlowResumeRatio/100.0)); - uint32_t maxMsgCount = getCapacity(settings, QueuePolicy::maxCountKey, 0); // no size by default + uint32_t maxMsgCount = settings.maxDepth.hasCount() ? settings.maxDepth.getCount() : 0; uint32_t flowStopCount = (uint32_t)(maxMsgCount * (defaultFlowStopRatio/100.0) + 0.5); uint32_t flowResumeCount = (uint32_t)(maxMsgCount * (defaultFlowResumeRatio/100.0)); - return new QueueFlowLimit(queue, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize); } return 0; @@ -346,7 +319,7 @@ void QueueFlowLimit::getState(qpid::framing::FieldTable& state ) const framing::SequenceSet ss; if (!index.empty()) { /* replicate the set of messages pending flow control */ - for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::const_iterator itr = index.begin(); + for (std::map<framing::SequenceNumber, Message >::const_iterator itr = index.begin(); itr != index.end(); ++itr) { ss.add(itr->first); } @@ -377,10 +350,10 @@ void QueueFlowLimit::setState(const qpid::framing::FieldTable& state) ++i; fcmsg.add(first, last); for (SequenceNumber seq = first; seq <= last; ++seq) { - QueuedMessage msg; + Message msg; queue->find(seq, msg); // fyi: may not be found if msg is acquired & unacked bool unique; - unique = index.insert(std::pair<framing::SequenceNumber, boost::intrusive_ptr<Message> >(seq, msg.payload)).second; + unique = index.insert(std::pair<framing::SequenceNumber, Message >(seq, msg)).second; // Like this to avoid tripping up unused variable warning when NDEBUG set if (!unique) assert(unique); } diff --git a/cpp/src/qpid/broker/QueueFlowLimit.h b/cpp/src/qpid/broker/QueueFlowLimit.h index ad8a2720ef..1bcc388ceb 100644 --- a/cpp/src/qpid/broker/QueueFlowLimit.h +++ b/cpp/src/qpid/broker/QueueFlowLimit.h @@ -26,9 +26,9 @@ #include <iostream> #include <memory> #include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/QueuedMessage.h" #include "qpid/broker/StatefulQueueObserver.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/SequenceNumber.h" #include "qpid/sys/AtomicValue.h" #include "qpid/sys/Mutex.h" @@ -45,6 +45,8 @@ namespace qpid { namespace broker { class Broker; +class Queue; +struct QueueSettings; /** * Producer flow control: when level is > flowStop*, flow control is ON. @@ -80,13 +82,13 @@ class Broker; QPID_BROKER_EXTERN virtual ~QueueFlowLimit(); - /** the queue has added QueuedMessage. Returns true if flow state changes */ - QPID_BROKER_EXTERN void enqueued(const QueuedMessage&); - /** the queue has removed QueuedMessage. Returns true if flow state changes */ - QPID_BROKER_EXTERN void dequeued(const QueuedMessage&); + /** the queue has added QueuedMessage */ + QPID_BROKER_EXTERN void enqueued(const Message&); + /** the queue has removed QueuedMessage */ + QPID_BROKER_EXTERN void dequeued(const Message&); /** ignored */ - QPID_BROKER_EXTERN void acquired(const QueuedMessage&) {}; - QPID_BROKER_EXTERN void requeued(const QueuedMessage&) {}; + QPID_BROKER_EXTERN void acquired(const Message&) {}; + QPID_BROKER_EXTERN void requeued(const Message&) {}; /** for clustering: */ QPID_BROKER_EXTERN void getState(qpid::framing::FieldTable&) const; @@ -106,14 +108,14 @@ class Broker; void decode(framing::Buffer& buffer); uint32_t encodedSize() const; - static QPID_BROKER_EXTERN void observe(Queue& queue, const qpid::framing::FieldTable& settings); + static QPID_BROKER_EXTERN void observe(Queue& queue, const QueueSettings& settings); static QPID_BROKER_EXTERN void setDefaults(uint64_t defaultMaxSize, uint defaultFlowStopRatio, uint defaultFlowResumeRatio); friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueueFlowLimit&); protected: // msgs waiting for flow to become available. - std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> > index; + std::map<framing::SequenceNumber, Message > index; mutable qpid::sys::Mutex indexLock; _qmfBroker::Queue *queueMgmtObj; @@ -123,7 +125,7 @@ class Broker; QPID_BROKER_EXTERN QueueFlowLimit(Queue *queue, uint32_t flowStopCount, uint32_t flowResumeCount, uint64_t flowStopSize, uint64_t flowResumeSize); - static QPID_BROKER_EXTERN QueueFlowLimit *createLimit(Queue *queue, const qpid::framing::FieldTable& settings); + static QPID_BROKER_EXTERN QueueFlowLimit *createLimit(Queue *queue, const QueueSettings& settings); }; }} diff --git a/cpp/src/qpid/broker/QueueObserver.h b/cpp/src/qpid/broker/QueueObserver.h index b58becd2ae..29e867253e 100644 --- a/cpp/src/qpid/broker/QueueObserver.h +++ b/cpp/src/qpid/broker/QueueObserver.h @@ -24,8 +24,8 @@ namespace qpid { namespace broker { -struct QueuedMessage; class Consumer; +class Message; /** * Interface for notifying classes who want to act as 'observers' of a queue of particular @@ -63,10 +63,10 @@ class QueueObserver virtual ~QueueObserver() {} // note: the Queue will hold the messageLock while calling these methods! - virtual void enqueued(const QueuedMessage&) = 0; - virtual void dequeued(const QueuedMessage&) = 0; - virtual void acquired(const QueuedMessage&) = 0; - virtual void requeued(const QueuedMessage&) = 0; + virtual void enqueued(const Message&) = 0; + virtual void dequeued(const Message&) = 0; + virtual void acquired(const Message&) = 0; + virtual void requeued(const Message&) = 0; virtual void consumerAdded( const Consumer& ) {}; virtual void consumerRemoved( const Consumer& ) {}; private: diff --git a/cpp/src/qpid/broker/QueuePolicy.cpp b/cpp/src/qpid/broker/QueuePolicy.cpp deleted file mode 100644 index 3978420f4e..0000000000 --- a/cpp/src/qpid/broker/QueuePolicy.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/broker/QueuePolicy.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/PriorityQueue.h" -#include "qpid/Exception.h" -#include "qpid/framing/FieldValue.h" -#include "qpid/framing/reply_exceptions.h" -#include "qpid/log/Statement.h" -#include <sstream> - -using namespace qpid::broker; -using namespace qpid::framing; - -QueuePolicy::QueuePolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : - maxCount(_maxCount), maxSize(_maxSize), type(_type), count(0), size(0), policyExceeded(false), queue(0), name(_name) { - QPID_LOG(info, "Queue \"" << name << "\": Policy created: type=" << type << "; maxCount=" << maxCount << "; maxSize=" << maxSize); -} - -void QueuePolicy::enqueued(uint64_t _size) -{ - if (maxCount) ++count; - if (maxSize) size += _size; -} - -void QueuePolicy::dequeued(uint64_t _size) -{ - if (maxCount) { - if (count > 0) { - --count; - } else { - throw Exception(QPID_MSG("Attempted count underflow on dequeue(" << _size << "): " << *this)); - } - } - if (maxSize) { - if (_size > size) { - throw Exception(QPID_MSG("Attempted size underflow on dequeue(" << _size << "): " << *this)); - } else { - size -= _size; - } - } -} - -bool QueuePolicy::checkLimit(boost::intrusive_ptr<Message> m) -{ - bool sizeExceeded = maxSize && (size + m->contentSize()) > maxSize; - bool countExceeded = maxCount && (count + 1) > maxCount; - bool exceeded = sizeExceeded || countExceeded; - if (exceeded) { - if (!policyExceeded) { - policyExceeded = true; - if (sizeExceeded) QPID_LOG(info, "Queue cumulative message size exceeded policy for " << name); - if (countExceeded) QPID_LOG(info, "Queue message count exceeded policy for " << name); - } - } else { - if (policyExceeded) { - policyExceeded = false; - QPID_LOG(info, "Queue cumulative message size and message count within policy for " << name); - } - } - return !exceeded; -} - -void QueuePolicy::tryEnqueue(boost::intrusive_ptr<Message> m) -{ - if (checkLimit(m)) { - enqueued(m->contentSize()); - } else { - throw ResourceLimitExceededException(QPID_MSG("Policy exceeded on " << name << ", policy: " << *this)); - } -} - -void QueuePolicy::recoverEnqueued(boost::intrusive_ptr<Message> m) -{ - tryEnqueue(m); -} - -void QueuePolicy::enqueueAborted(boost::intrusive_ptr<Message> m) -{ - dequeued(m->contentSize()); -} - -void QueuePolicy::enqueued(const QueuedMessage&) {} - -void QueuePolicy::dequeued(const QueuedMessage& m) -{ - dequeued(m.payload->contentSize()); -} - -bool QueuePolicy::isEnqueued(const QueuedMessage&) -{ - return true; -} - -void QueuePolicy::update(FieldTable& settings) -{ - if (maxCount) settings.setInt(maxCountKey, maxCount); - if (maxSize) settings.setInt(maxSizeKey, maxSize); - settings.setString(typeKey, type); -} - -template <typename T> -T getCapacity(const FieldTable& settings, const std::string& key, T defaultValue) -{ - FieldTable::ValuePtr v = settings.get(key); - - T result = 0; - - if (!v) return defaultValue; - if (v->getType() == 0x23) { - QPID_LOG(debug, "Value for " << key << " specified as float: " << v->get<float>()); - } else if (v->getType() == 0x33) { - QPID_LOG(debug, "Value for " << key << " specified as double: " << v->get<double>()); - } else if (v->convertsTo<T>()) { - result = v->get<T>(); - QPID_LOG(debug, "Got integer value for " << key << ": " << result); - if (result >= 0) return result; - } else if (v->convertsTo<std::string>()) { - std::string s(v->get<std::string>()); - QPID_LOG(debug, "Got string value for " << key << ": " << s); - std::istringstream convert(s); - if (convert >> result && result >= 0 && convert.eof()) return result; - } - - throw IllegalArgumentException(QPID_MSG("Cannot convert " << key << " to unsigned integer: " << *v)); -} - -std::string QueuePolicy::getType(const FieldTable& settings) -{ - FieldTable::ValuePtr v = settings.get(typeKey); - if (v && v->convertsTo<std::string>()) { - std::string t = v->get<std::string>(); - std::transform(t.begin(), t.end(), t.begin(), tolower); - if (t == REJECT || t == FLOW_TO_DISK || t == RING || t == RING_STRICT) return t; - } - return REJECT; -} - -void QueuePolicy::setDefaultMaxSize(uint64_t s) -{ - defaultMaxSize = s; -} - -void QueuePolicy::getPendingDequeues(Messages&) {} - - - - -void QueuePolicy::encode(Buffer& buffer) const -{ - buffer.putLong(maxCount); - buffer.putLongLong(maxSize); - buffer.putLong(count); - buffer.putLongLong(size); -} - -void QueuePolicy::decode ( Buffer& buffer ) -{ - maxCount = buffer.getLong(); - maxSize = buffer.getLongLong(); - count = buffer.getLong(); - size = buffer.getLongLong(); -} - - -uint32_t QueuePolicy::encodedSize() const { - return sizeof(uint32_t) + // maxCount - sizeof(uint64_t) + // maxSize - sizeof(uint32_t) + // count - sizeof(uint64_t); // size -} - - - -const std::string QueuePolicy::maxCountKey("qpid.max_count"); -const std::string QueuePolicy::maxSizeKey("qpid.max_size"); -const std::string QueuePolicy::typeKey("qpid.policy_type"); -const std::string QueuePolicy::REJECT("reject"); -const std::string QueuePolicy::FLOW_TO_DISK("flow_to_disk"); -const std::string QueuePolicy::RING("ring"); -const std::string QueuePolicy::RING_STRICT("ring_strict"); -uint64_t QueuePolicy::defaultMaxSize(0); - -FlowToDiskPolicy::FlowToDiskPolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize) : - QueuePolicy(_name, _maxCount, _maxSize, FLOW_TO_DISK) {} - -bool FlowToDiskPolicy::checkLimit(boost::intrusive_ptr<Message> m) -{ - if (!QueuePolicy::checkLimit(m)) { - m->requestContentRelease(); - if (queue) - queue->countFlowedToDisk(m->contentSize()); - } - return true; -} - -RingQueuePolicy::RingQueuePolicy(const std::string& _name, - uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : - QueuePolicy(_name, _maxCount, _maxSize, _type), strict(_type == RING_STRICT) {} - -bool before(const QueuedMessage& a, const QueuedMessage& b) -{ - int priorityA = PriorityQueue::getPriority(a); - int priorityB = PriorityQueue::getPriority(b); - if (priorityA == priorityB) return a.position < b.position; - else return priorityA < priorityB; -} - -void RingQueuePolicy::enqueued(const QueuedMessage& m) -{ - //need to insert in correct location based on position - queue.insert(lower_bound(queue.begin(), queue.end(), m, before), m); -} - -void RingQueuePolicy::dequeued(const QueuedMessage& m) -{ - //find and remove m from queue - if (find(m, pendingDequeues, true) || find(m, queue, true)) { - //now update count and size - QueuePolicy::dequeued(m); - } -} - -bool RingQueuePolicy::isEnqueued(const QueuedMessage& m) -{ - //for non-strict ring policy, a message can be replaced (and - //therefore dequeued) before it is accepted or released by - //subscriber; need to detect this - return find(m, pendingDequeues, false) || find(m, queue, false); -} - -bool RingQueuePolicy::checkLimit(boost::intrusive_ptr<Message> m) -{ - - // If the message is bigger than the queue size, give up - if (getMaxSize() && m->contentSize() > getMaxSize()) { - QPID_LOG(debug, "Message too large for ring queue " << name - << " [" << *this << "] " - << ": message size = " << m->contentSize() << " bytes" - << ": max queue size = " << getMaxSize() << " bytes"); - return false; - } - - // if within limits, ok to accept - if (QueuePolicy::checkLimit(m)) return true; - - // At this point, we've exceeded maxSize, maxCount, or both. - // - // If we've exceeded maxCount, we've exceeded it by 1, so - // replacing the first message is sufficient. If we've exceeded - // maxSize, we need to pop enough messages to get the space we - // need. - - unsigned int haveSpace = getMaxSize() - getCurrentQueueSize(); - - do { - QueuedMessage oldest = queue.front(); - if (oldest.queue->acquireMessageAt(oldest.position, oldest) || !strict) { - queue.pop_front(); - pendingDequeues.push_back(oldest); - QPID_LOG(debug, "Ring policy triggered in " << name - << ": removed message " << oldest.position << " to make way for new message"); - - haveSpace += oldest.payload->contentSize(); - - } else { - //in strict mode, if oldest message has been delivered (hence - //cannot be acquired) but not yet acked, it should not be - //removed and the attempted enqueue should fail - QPID_LOG(debug, "Ring policy could not be triggered in " << name - << ": oldest message (seq-no=" << oldest.position << ") has been delivered but not yet acknowledged or requeued"); - return false; - } - } while (getMaxSize() && haveSpace < m->contentSize()); - - - return true; -} - -void RingQueuePolicy::getPendingDequeues(Messages& result) -{ - result = pendingDequeues; -} - -bool RingQueuePolicy::find(const QueuedMessage& m, Messages& q, bool remove) -{ - for (Messages::iterator i = q.begin(); i != q.end(); i++) { - if (i->payload == m.payload) { - if (remove) q.erase(i); - return true; - } - } - return false; -} - -std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type) -{ - return createQueuePolicy("<unspecified>", maxCount, maxSize, type); -} - -std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const qpid::framing::FieldTable& settings) -{ - return createQueuePolicy("<unspecified>", settings); -} - -std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings) -{ - uint32_t maxCount = getCapacity<int32_t>(settings, maxCountKey, 0); - uint64_t maxSize = getCapacity<int64_t>(settings, maxSizeKey, defaultMaxSize); - if (maxCount || maxSize) { - return createQueuePolicy(name, maxCount, maxSize, getType(settings)); - } else { - return std::auto_ptr<QueuePolicy>(); - } -} - -std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name, - uint32_t maxCount, uint64_t maxSize, const std::string& type) -{ - if (type == RING || type == RING_STRICT) { - return std::auto_ptr<QueuePolicy>(new RingQueuePolicy(name, maxCount, maxSize, type)); - } else if (type == FLOW_TO_DISK) { - return std::auto_ptr<QueuePolicy>(new FlowToDiskPolicy(name, maxCount, maxSize)); - } else { - return std::auto_ptr<QueuePolicy>(new QueuePolicy(name, maxCount, maxSize, type)); - } - -} - -namespace qpid { - namespace broker { - -std::ostream& operator<<(std::ostream& out, const QueuePolicy& p) -{ - if (p.maxSize) out << "size: max=" << p.maxSize << ", current=" << p.size; - else out << "size: unlimited"; - out << "; "; - if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count; - else out << "count: unlimited"; - out << "; type=" << p.type; - return out; -} - - } -} - diff --git a/cpp/src/qpid/broker/QueuePolicy.h b/cpp/src/qpid/broker/QueuePolicy.h deleted file mode 100644 index f23b709f18..0000000000 --- a/cpp/src/qpid/broker/QueuePolicy.h +++ /dev/null @@ -1,126 +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 _QueuePolicy_ -#define _QueuePolicy_ - -#include <deque> -#include <iostream> -#include <memory> -#include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/QueuedMessage.h" -#include "qpid/framing/FieldTable.h" -#include "qpid/sys/AtomicValue.h" -#include "qpid/sys/Mutex.h" - -namespace qpid { -namespace broker { - -class Queue; - -class QueuePolicy -{ - static uint64_t defaultMaxSize; - - uint32_t maxCount; - uint64_t maxSize; - const std::string type; - uint32_t count; - uint64_t size; - bool policyExceeded; - - protected: - Queue* queue; - uint64_t getCurrentQueueSize() const { return size; } - - public: - typedef std::deque<QueuedMessage> Messages; - static QPID_BROKER_EXTERN const std::string maxCountKey; - static QPID_BROKER_EXTERN const std::string maxSizeKey; - static QPID_BROKER_EXTERN const std::string typeKey; - static QPID_BROKER_EXTERN const std::string REJECT; - static QPID_BROKER_EXTERN const std::string FLOW_TO_DISK; - static QPID_BROKER_EXTERN const std::string RING; - static QPID_BROKER_EXTERN const std::string RING_STRICT; - - virtual ~QueuePolicy() {} - QPID_BROKER_EXTERN void tryEnqueue(boost::intrusive_ptr<Message> msg); - QPID_BROKER_EXTERN void recoverEnqueued(boost::intrusive_ptr<Message> msg); - QPID_BROKER_EXTERN void enqueueAborted(boost::intrusive_ptr<Message> msg); - virtual void enqueued(const QueuedMessage&); - virtual void dequeued(const QueuedMessage&); - virtual bool isEnqueued(const QueuedMessage&); - QPID_BROKER_EXTERN void update(qpid::framing::FieldTable& settings); - uint32_t getMaxCount() const { return maxCount; } - uint64_t getMaxSize() const { return maxSize; } - void encode(framing::Buffer& buffer) const; - void decode ( framing::Buffer& buffer ); - uint32_t encodedSize() const; - virtual void getPendingDequeues(Messages& result); - std::string getType() const { return type; } - void setQueue(Queue* q) { queue = q; } - - static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings); - static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); - static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const qpid::framing::FieldTable& settings); - static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); - static std::string getType(const qpid::framing::FieldTable& settings); - static void setDefaultMaxSize(uint64_t); - friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, - const QueuePolicy&); - protected: - const std::string name; - - QueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); - - virtual bool checkLimit(boost::intrusive_ptr<Message> msg); - void enqueued(uint64_t size); - void dequeued(uint64_t size); -}; - - -class FlowToDiskPolicy : public QueuePolicy -{ - public: - FlowToDiskPolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize); - bool checkLimit(boost::intrusive_ptr<Message> msg); -}; - -class RingQueuePolicy : public QueuePolicy -{ - public: - RingQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = RING); - void enqueued(const QueuedMessage&); - void dequeued(const QueuedMessage&); - bool isEnqueued(const QueuedMessage&); - bool checkLimit(boost::intrusive_ptr<Message> msg); - void getPendingDequeues(Messages& result); - private: - Messages pendingDequeues; - Messages queue; - const bool strict; - - bool find(const QueuedMessage&, Messages&, bool remove); -}; - -}} - - -#endif diff --git a/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp index 1401356444..3521e08325 100644 --- a/cpp/src/qpid/broker/QueueRegistry.cpp +++ b/cpp/src/qpid/broker/QueueRegistry.cpp @@ -21,7 +21,6 @@ #include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" #include "qpid/broker/QueueRegistry.h" -#include "qpid/broker/QueueEvents.h" #include "qpid/broker/Exchange.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" @@ -32,50 +31,43 @@ using namespace qpid::broker; using namespace qpid::sys; using std::string; -QueueRegistry::QueueRegistry(Broker* b) : - counter(1), store(0), events(0), parent(0), lastNode(false), broker(b) {} +QueueRegistry::QueueRegistry(Broker* b) +{ + setBroker(b); +} QueueRegistry::~QueueRegistry(){} std::pair<Queue::shared_ptr, bool> -QueueRegistry::declare(const string& declareName, bool durable, - bool autoDelete, const OwnershipToken* owner, +QueueRegistry::declare(const string& name, const QueueSettings& settings, boost::shared_ptr<Exchange> alternate, - const qpid::framing::FieldTable& arguments, bool recovering/*true if this declare is a result of recovering queue - definition from persistente + definition from persistent record*/) { - Queue::shared_ptr queue; std::pair<Queue::shared_ptr, bool> result; { RWlock::ScopedWlock locker(lock); - string name = declareName.empty() ? generateName() : declareName; - assert(!name.empty()); QueueMap::iterator i = queues.find(name); - if (i == queues.end()) { - queue.reset(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker)); + Queue::shared_ptr queue = create(name, settings); + //Move this to factory also? if (alternate) { queue->setAlternateExchange(alternate);//need to do this *before* create alternate->incAlternateUsers(); } if (!recovering) { - //apply settings & create persistent record if required - queue->create(arguments); - } else { - //i.e. recovering a queue for which we already have a persistent record - queue->configure(arguments); + //create persistent record if required + queue->create(); } queues[name] = queue; - if (lastNode) queue->setLastNodeFailure(); result = std::pair<Queue::shared_ptr, bool>(queue, true); } else { result = std::pair<Queue::shared_ptr, bool>(i->second, false); } } - if (broker && queue) broker->getConfigurationObservers().queueCreate(queue); + if (getBroker() && result.second) getBroker()->getConfigurationObservers().queueCreate(result.first); return result; } @@ -85,11 +77,11 @@ void QueueRegistry::destroy(const string& name) { qpid::sys::RWlock::ScopedWlock locker(lock); QueueMap::iterator i = queues.find(name); if (i != queues.end()) { - Queue::shared_ptr q = i->second; + q = i->second; queues.erase(i); } } - if (broker && q) broker->getConfigurationObservers().queueDestroy(q); + if (getBroker() && q) getBroker()->getConfigurationObservers().queueDestroy(q); } Queue::shared_ptr QueueRegistry::find(const string& name){ @@ -108,36 +100,17 @@ Queue::shared_ptr QueueRegistry::get(const string& name) { return q; } -string QueueRegistry::generateName(){ - string name; - do { - std::stringstream ss; - ss << "tmp_" << counter++; - name = ss.str(); - // Thread safety: Private function, only called with lock held - // so this is OK. - } while(queues.find(name) != queues.end()); - return name; -} - void QueueRegistry::setStore (MessageStore* _store) { - store = _store; + QueueFactory::setStore(_store); } -MessageStore* QueueRegistry::getStore() const { - return store; +MessageStore* QueueRegistry::getStore() const +{ + return QueueFactory::getStore(); } -void QueueRegistry::updateQueueClusterState(bool _lastNode) +void QueueRegistry::setParent(qpid::management::Manageable* _parent) { - RWlock::ScopedRlock locker(lock); - for (QueueMap::iterator i = queues.begin(); i != queues.end(); i++) { - if (_lastNode){ - i->second->setLastNodeFailure(); - } else { - i->second->clearLastNodeFailure(); - } - } - lastNode = _lastNode; + QueueFactory::setParent(_parent); } diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h index a354513c5f..7fce90c679 100644 --- a/cpp/src/qpid/broker/QueueRegistry.h +++ b/cpp/src/qpid/broker/QueueRegistry.h @@ -22,8 +22,8 @@ #define _QueueRegistry_ #include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/QueueFactory.h" #include "qpid/sys/Mutex.h" -#include "qpid/management/Manageable.h" #include "qpid/framing/FieldTable.h" #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> @@ -34,11 +34,8 @@ namespace qpid { namespace broker { class Queue; -class QueueEvents; class Exchange; class OwnershipToken; -class Broker; -class MessageStore; /** * A registry of queues indexed by queue name. @@ -47,7 +44,7 @@ class MessageStore; * are deleted when and only when they are no longer in use. * */ -class QueueRegistry { +class QueueRegistry : QueueFactory { public: QPID_BROKER_EXTERN QueueRegistry(Broker* b = 0); QPID_BROKER_EXTERN ~QueueRegistry(); @@ -60,11 +57,8 @@ class QueueRegistry { */ QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Queue>, bool> declare( const std::string& name, - bool durable = false, - bool autodelete = false, - const OwnershipToken* owner = 0, + const QueueSettings& settings, boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(), - const qpid::framing::FieldTable& args = framing::FieldTable(), bool recovering = false); /** @@ -101,11 +95,6 @@ class QueueRegistry { QPID_BROKER_EXTERN boost::shared_ptr<Queue> get(const std::string& name); /** - * Generate unique queue name. - */ - std::string generateName(); - - /** * Set the store to use. May only be called once. */ void setStore (MessageStore*); @@ -118,7 +107,7 @@ class QueueRegistry { /** * Register the manageable parent for declared queues */ - void setParent (management::Manageable* _parent) { parent = _parent; } + void setParent (management::Manageable*); /** Call f for each queue in the registry. */ template <class F> void eachQueue(F f) const { @@ -127,22 +116,10 @@ class QueueRegistry { f(i->second); } - /** - * Change queue mode when cluster size drops to 1 node, expands again - * in practice allows flow queue to disk when last name to be exectuted - */ - void updateQueueClusterState(bool lastNode); - private: typedef std::map<std::string, boost::shared_ptr<Queue> > QueueMap; QueueMap queues; mutable qpid::sys::RWlock lock; - int counter; - MessageStore* store; - QueueEvents* events; - management::Manageable* parent; - bool lastNode; //used to set mode on queue declare - Broker* broker; }; diff --git a/cpp/src/qpid/broker/QueueSettings.cpp b/cpp/src/qpid/broker/QueueSettings.cpp new file mode 100644 index 0000000000..91616636f1 --- /dev/null +++ b/cpp/src/qpid/broker/QueueSettings.cpp @@ -0,0 +1,228 @@ +/* + * + * 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 "QueueSettings.h" +#include "QueueFlowLimit.h" +#include "MessageGroupManager.h" +#include "qpid/types/Variant.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include "qpid/amqp_0_10/Codecs.h" + + +namespace qpid { +namespace broker { + +namespace { +const std::string MAX_COUNT("qpid.max_count"); +const std::string MAX_SIZE("qpid.max_size"); +const std::string POLICY_TYPE("qpid.policy_type"); +const std::string POLICY_TYPE_REJECT("reject"); +const std::string POLICY_TYPE_RING("ring"); +const std::string NO_LOCAL("no-local"); +const std::string TRACE_ID("qpid.trace.id"); +const std::string TRACE_EXCLUDES("qpid.trace.exclude"); +const std::string LVQ_KEY("qpid.last_value_queue_key"); +const std::string AUTO_DELETE_TIMEOUT("qpid.auto_delete_timeout"); +const std::string ALERT_REPEAT_GAP("qpid.alert_repeat_gap"); +const std::string ALERT_COUNT("qpid.alert_count"); +const std::string ALERT_SIZE("qpid.alert_size"); +const std::string PRIORITIES("qpid.priorities"); +const std::string FAIRSHARE("qpid.fairshare"); +const std::string FAIRSHARE_ALIAS("x-qpid-fairshare"); + +const std::string LVQ_LEGACY("qpid.last_value_queue"); +const std::string LVQ_LEGACY_KEY("qpid.LVQ_key"); +const std::string LVQ_LEGACY_NOBROWSE("qpid.last_value_queue_no_browse"); + + +bool handleFairshareSetting(const std::string& basename, const std::string& key, const qpid::types::Variant& value, QueueSettings& settings) +{ + if (key.find(basename) == 0) { + qpid::types::Variant index(key.substr(basename.size()+1)); + settings.fairshare[index] = value; + return true; + } else { + return false; + } +} +bool isFairshareSetting(const std::string& key, const qpid::types::Variant& value, QueueSettings& settings) +{ + return handleFairshareSetting(FAIRSHARE, key, value, settings) || handleFairshareSetting(FAIRSHARE_ALIAS, key, value, settings); +} +} + +const QueueSettings::Aliases QueueSettings::aliases; + +QueueSettings::QueueSettings(bool d, bool a) : + durable(d), + autodelete(a), + priorities(0), + defaultFairshare(0), + shareGroups(false), + addTimestamp(false), + dropMessagesAtLimit(false), + noLocal(false), + autoDeleteDelay(0), + alertRepeatInterval(60) +{} + +bool QueueSettings::handle(const std::string& key, const qpid::types::Variant& value) +{ + if (key == MAX_COUNT && value.asUint32() > 0) { + maxDepth.setCount(value); + return true; + } else if (key == MAX_SIZE && value.asUint64() > 0) { + maxDepth.setSize(value); + return true; + } else if (key == POLICY_TYPE) { + if (value.getString() == POLICY_TYPE_RING) { + dropMessagesAtLimit = true; + return true; + } else if (value.getString() == POLICY_TYPE_REJECT) { + //do nothing, thats the default + return true; + } else { + QPID_LOG(warning, "Unrecognised policy option: " << value); + return false; + } + } else if (key == NO_LOCAL) { + noLocal = value; + return true; + } else if (key == TRACE_ID) { + traceId = value.asString(); + return true; + } else if (key == TRACE_EXCLUDES) { + traceExcludes = value.asString(); + return true; + } else if (key == PRIORITIES) { + priorities = value; + return true; + } else if (key == FAIRSHARE) { + defaultFairshare = value; + return true; + } else if (isFairshareSetting(key, value, *this)) { + return true; + } else if (key == MessageGroupManager::qpidMessageGroupKey) { + groupKey = value.asString(); + return true; + } else if (key == MessageGroupManager::qpidSharedGroup) { + shareGroups = value; + return true; + } else if (key == MessageGroupManager::qpidMessageGroupTimestamp) { + addTimestamp = value; + return true; + } else if (key == LVQ_KEY) { + lvqKey = value.asString(); + return true; + } else if (key == LVQ_LEGACY) { + if (lvqKey.empty()) lvqKey = LVQ_LEGACY_KEY; + return true; + } else if (key == LVQ_LEGACY_NOBROWSE) { + QPID_LOG(warning, "Ignoring 'no-browse' directive for LVQ; it is no longer necessary"); + if (lvqKey.empty()) lvqKey = LVQ_LEGACY_KEY; + return true; + } else if (key == AUTO_DELETE_TIMEOUT) { + autoDeleteDelay = value; + return true; + } else if (key == QueueFlowLimit::flowStopCountKey) { + flowStop.setCount(value); + return true; + } else if (key == QueueFlowLimit::flowResumeCountKey) { + flowResume.setCount(value); + return true; + } else if (key == QueueFlowLimit::flowStopSizeKey) { + flowStop.setSize(value); + return true; + } else if (key == QueueFlowLimit::flowResumeSizeKey) { + flowResume.setSize(value); + return true; + } else if (key == ALERT_REPEAT_GAP) { + alertRepeatInterval = value; + return true; + } else if (key == ALERT_COUNT) { + alertThreshold.setCount(value); + return true; + } else if (key == ALERT_SIZE) { + alertThreshold.setSize(value); + return true; + } else { + return false; + } +} + +void QueueSettings::validate() const +{ + if (lvqKey.size() && priorities > 0) + throw qpid::framing::InvalidArgumentException(QPID_MSG("Cannot specify " << LVQ_KEY << " and " << PRIORITIES << " for the same queue")); + if ((fairshare.size() || defaultFairshare) && priorities == 0) + throw qpid::framing::InvalidArgumentException(QPID_MSG("Cannot specify fairshare settings when queue is not enabled for priorities")); + if (fairshare.size() > priorities) + throw qpid::framing::InvalidArgumentException(QPID_MSG("Cannot have fairshare set for priority levels greater than " << priorities)); + if (groupKey.size() && lvqKey.size()) + throw qpid::framing::InvalidArgumentException(QPID_MSG("Cannot specify " << LVQ_KEY << " and " << MessageGroupManager::qpidMessageGroupKey << " for the same queue")); + if (groupKey.size() && priorities) + throw qpid::framing::InvalidArgumentException(QPID_MSG("Cannot specify " << PRIORITIES << " and " << MessageGroupManager::qpidMessageGroupKey << " for the same queue")); + if (shareGroups && groupKey.empty()) { + throw qpid::framing::InvalidArgumentException(QPID_MSG("Can only specify " << MessageGroupManager::qpidSharedGroup + << " if " << MessageGroupManager::qpidMessageGroupKey << " is set")); + } + if (addTimestamp && groupKey.empty()) { + throw qpid::framing::InvalidArgumentException(QPID_MSG("Can only specify " << MessageGroupManager::qpidMessageGroupTimestamp + << " if " << MessageGroupManager::qpidMessageGroupKey << " is set")); + } + + // @todo: remove once "sticky" consumers are supported - see QPID-3347 + if (!shareGroups && groupKey.size()) { + throw qpid::framing::InvalidArgumentException(QPID_MSG("Only shared groups are supported at present; " << MessageGroupManager::qpidSharedGroup + << " is required if " << MessageGroupManager::qpidMessageGroupKey << " is set")); + } +} + +void QueueSettings::populate(const std::map<std::string, qpid::types::Variant>& inputs, std::map<std::string, qpid::types::Variant>& unused) +{ + original = inputs; + for (qpid::types::Variant::Map::const_iterator i = inputs.begin(); i != inputs.end(); ++i) { + Aliases::const_iterator a = aliases.find(i->first); + if (!handle((a != aliases.end() ? a->second : i->first), i->second)) unused.insert(*i); + } +} +void QueueSettings::populate(const qpid::framing::FieldTable& inputs, qpid::framing::FieldTable& unused) +{ + qpid::types::Variant::Map o; + qpid::amqp_0_10::translate(inputs, original); + populate(original, o); + qpid::amqp_0_10::translate(o, unused); +} +std::map<std::string, qpid::types::Variant> QueueSettings::asMap() const +{ + return original; +} + +QueueSettings::Aliases::Aliases() +{ + insert(value_type("x-qpid-priorities", "qpid.priorities")); + insert(value_type("x-qpid-fairshare", "qpid.fairshare")); + insert(value_type("x-qpid-minimum-alert-repeat-gap", "qpid.alert_repeat_gap")); + insert(value_type("x-qpid-maximum-message-count", "qpid.alert_count")); + insert(value_type("x-qpid-maximum-message-size", "qpid.alert_size")); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueSettings.h b/cpp/src/qpid/broker/QueueSettings.h new file mode 100644 index 0000000000..2443624615 --- /dev/null +++ b/cpp/src/qpid/broker/QueueSettings.h @@ -0,0 +1,92 @@ +#ifndef QPID_BROKER_QUEUESETTINGS_H +#define QPID_BROKER_QUEUESETTINGS_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/BrokerImportExport.h" +#include "qpid/broker/QueueDepth.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/framing/FieldTable.h" +#include <string> +#include <map> + +namespace qpid { +namespace types { +class Variant; +} +namespace broker { + +/** + * Defines the various queue configuration settings that can be specified + */ +struct QueueSettings +{ + QPID_BROKER_EXTERN QueueSettings(bool durable=false, bool autodelete=false); + + bool durable; + bool autodelete; + + //basic queue types: + std::string lvqKey; + uint32_t priorities; + uint32_t defaultFairshare; + std::map<uint32_t,uint32_t> fairshare; + + //message groups: + std::string groupKey; + bool shareGroups; + bool addTimestamp;//not actually used; always on at present? + + QueueDepth maxDepth; + bool dropMessagesAtLimit;//aka ring queue policy + + bool noLocal; + std::string traceId; + std::string traceExcludes; + uint64_t autoDeleteDelay;//queueTtl? + + //flow control: + QueueDepth flowStop; + QueueDepth flowResume; + + //threshold events: + QueueDepth alertThreshold; + int64_t alertRepeatInterval; + + //yuck, yuck + qpid::framing::FieldTable storeSettings; + std::map<std::string, qpid::types::Variant> original; + + bool handle(const std::string& key, const qpid::types::Variant& value); + void validate() const; + QPID_BROKER_EXTERN void populate(const std::map<std::string, qpid::types::Variant>& inputs, std::map<std::string, qpid::types::Variant>& unused); + QPID_BROKER_EXTERN void populate(const qpid::framing::FieldTable& inputs, qpid::framing::FieldTable& unused); + std::map<std::string, qpid::types::Variant> asMap() const; + + struct Aliases : std::map<std::string, std::string> + { + Aliases(); + }; + static const Aliases aliases; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUESETTINGS_H*/ diff --git a/cpp/src/qpid/broker/QueuedMessage.h b/cpp/src/qpid/broker/QueuedMessage.h index 9d008193a0..c80fff900a 100644 --- a/cpp/src/qpid/broker/QueuedMessage.h +++ b/cpp/src/qpid/broker/QueuedMessage.h @@ -22,8 +22,8 @@ #define _QueuedMessage_ #include "qpid/broker/Message.h" -#include "BrokerImportExport.h" -#include <iosfwd> +#include "qpid/framing/SequenceNumber.h" +#include "qpid/broker/BrokerImportExport.h" namespace qpid { namespace broker { @@ -32,20 +32,19 @@ class Queue; struct QueuedMessage { - boost::intrusive_ptr<Message> payload; + Message message; framing::SequenceNumber position; - typedef enum { AVAILABLE, ACQUIRED, DELETED, REMOVED } Status; - Status status; + enum {AVAILABLE, ACQUIRED, DELETED, REMOVED} status; Queue* queue; - QueuedMessage(Queue* q=0, - boost::intrusive_ptr<Message> msg=0, - framing::SequenceNumber sn=0, - Status st=AVAILABLE - ) : payload(msg), position(sn), status(st), queue(q) {} + QueuedMessage() : queue(0) {} + QueuedMessage(Queue* q, Message msg, framing::SequenceNumber sn) : + message(msg), position(sn), queue(q) {} + QueuedMessage(Queue* q) : queue(q) {} }; -inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) { +inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) +{ return a.position < b.position; } diff --git a/cpp/src/qpid/broker/RecoveredDequeue.cpp b/cpp/src/qpid/broker/RecoveredDequeue.cpp index cd6735328f..6e21a5bc21 100644 --- a/cpp/src/qpid/broker/RecoveredDequeue.cpp +++ b/cpp/src/qpid/broker/RecoveredDequeue.cpp @@ -22,10 +22,9 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/RecoveredDequeue.h" -using boost::intrusive_ptr; using namespace qpid::broker; -RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) +RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, Message _msg) : queue(_queue), msg(_msg) { queue->recoverPrepared(msg); } @@ -38,11 +37,11 @@ bool RecoveredDequeue::prepare(TransactionContext*) throw() void RecoveredDequeue::commit() throw() { - queue->enqueueAborted(msg); + queue->dequeueCommited(msg); } void RecoveredDequeue::rollback() throw() { - queue->process(msg); + queue->dequeueAborted(msg); } diff --git a/cpp/src/qpid/broker/RecoveredDequeue.h b/cpp/src/qpid/broker/RecoveredDequeue.h index 66e66f1d5f..87f768eefd 100644 --- a/cpp/src/qpid/broker/RecoveredDequeue.h +++ b/cpp/src/qpid/broker/RecoveredDequeue.h @@ -26,8 +26,6 @@ #include "qpid/broker/MessageStore.h" #include "qpid/broker/TxOp.h" -#include <boost/intrusive_ptr.hpp> - #include <algorithm> #include <functional> #include <list> @@ -36,18 +34,17 @@ namespace qpid { namespace broker { class RecoveredDequeue : public TxOp{ boost::shared_ptr<Queue> queue; - boost::intrusive_ptr<Message> msg; + Message msg; public: - RecoveredDequeue(boost::shared_ptr<Queue> queue, boost::intrusive_ptr<Message> msg); + RecoveredDequeue(boost::shared_ptr<Queue> queue, Message msg); virtual bool prepare(TransactionContext* ctxt) throw(); virtual void commit() throw(); virtual void rollback() throw(); virtual ~RecoveredDequeue(){} - virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } boost::shared_ptr<Queue> getQueue() const { return queue; } - boost::intrusive_ptr<Message> getMessage() const { return msg; } + Message getMessage() const { return msg; } }; } } diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/cpp/src/qpid/broker/RecoveredEnqueue.cpp index 6d2eaee6c4..296d5194c0 100644 --- a/cpp/src/qpid/broker/RecoveredEnqueue.cpp +++ b/cpp/src/qpid/broker/RecoveredEnqueue.cpp @@ -22,10 +22,9 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/RecoveredEnqueue.h" -using boost::intrusive_ptr; using namespace qpid::broker; -RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) +RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, Message _msg) : queue(_queue), msg(_msg) { queue->recoverPrepared(msg); } @@ -36,7 +35,7 @@ bool RecoveredEnqueue::prepare(TransactionContext*) throw(){ } void RecoveredEnqueue::commit() throw(){ - queue->process(msg); + queue->enqueueCommited(msg); } void RecoveredEnqueue::rollback() throw(){ diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.h b/cpp/src/qpid/broker/RecoveredEnqueue.h index 5f718001d5..d1f8e1106c 100644 --- a/cpp/src/qpid/broker/RecoveredEnqueue.h +++ b/cpp/src/qpid/broker/RecoveredEnqueue.h @@ -26,8 +26,6 @@ #include "qpid/broker/MessageStore.h" #include "qpid/broker/TxOp.h" -#include <boost/intrusive_ptr.hpp> - #include <algorithm> #include <functional> #include <list> @@ -36,19 +34,17 @@ namespace qpid { namespace broker { class RecoveredEnqueue : public TxOp{ boost::shared_ptr<Queue> queue; - boost::intrusive_ptr<Message> msg; + Message msg; public: - RecoveredEnqueue(boost::shared_ptr<Queue> queue, boost::intrusive_ptr<Message> msg); + RecoveredEnqueue(boost::shared_ptr<Queue> queue, Message msg); virtual bool prepare(TransactionContext* ctxt) throw(); virtual void commit() throw(); virtual void rollback() throw(); virtual ~RecoveredEnqueue(){} - virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } boost::shared_ptr<Queue> getQueue() const { return queue; } - boost::intrusive_ptr<Message> getMessage() const { return msg; } - + Message getMessage() const { return msg; } }; } } diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index 858535637a..7deeba5e65 100644 --- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -21,11 +21,13 @@ #include "qpid/broker/RecoveryManagerImpl.h" #include "qpid/broker/Message.h" +#include "qpid/broker/PersistableMessage.h" #include "qpid/broker/Queue.h" #include "qpid/broker/Link.h" #include "qpid/broker/Bridge.h" #include "qpid/broker/RecoveredEnqueue.h" #include "qpid/broker/RecoveredDequeue.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/reply_exceptions.h" using boost::dynamic_pointer_cast; @@ -43,9 +45,9 @@ RecoveryManagerImpl::~RecoveryManagerImpl() {} class RecoverableMessageImpl : public RecoverableMessage { - intrusive_ptr<Message> msg; + Message msg; public: - RecoverableMessageImpl(const intrusive_ptr<Message>& _msg); + RecoverableMessageImpl(const Message& _msg); ~RecoverableMessageImpl() {}; void setPersistenceId(uint64_t id); void setRedelivered(); @@ -128,9 +130,10 @@ RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer) { - boost::intrusive_ptr<Message> message(new Message()); - message->decodeHeader(buffer); - return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(message)); + //TODO: determine encoding/version actually used + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer()); + transfer->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer))); } RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid, @@ -163,12 +166,7 @@ void RecoveryManagerImpl::recoveryComplete() exchanges.eachExchange(boost::bind(&Exchange::recoveryComplete, _1, boost::ref(exchanges))); } -RecoverableMessageImpl:: RecoverableMessageImpl(const intrusive_ptr<Message>& _msg) : msg(_msg) -{ - if (!msg->isPersistent()) { - msg->forcePersistent(); // set so that message will get dequeued from store. - } -} +RecoverableMessageImpl:: RecoverableMessageImpl(const Message& _msg) : msg(_msg) {} bool RecoverableMessageImpl::loadContent(uint64_t /*available*/) { @@ -177,7 +175,7 @@ bool RecoverableMessageImpl::loadContent(uint64_t /*available*/) void RecoverableMessageImpl::decodeContent(framing::Buffer& buffer) { - msg->decodeContent(buffer); + msg.getPersistentContext()->decodeContent(buffer); } void RecoverableMessageImpl::recover(Queue::shared_ptr queue) @@ -187,12 +185,12 @@ void RecoverableMessageImpl::recover(Queue::shared_ptr queue) void RecoverableMessageImpl::setPersistenceId(uint64_t id) { - msg->setPersistenceId(id); + msg.getPersistentContext()->setPersistenceId(id); } void RecoverableMessageImpl::setRedelivered() { - msg->redeliver(); + msg.deliver();//increment delivery count (but at present that isn't recorded durably) } void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg) @@ -204,7 +202,7 @@ void RecoverableQueueImpl::setPersistenceId(uint64_t id) { queue->setPersistenceId(id); } - + uint64_t RecoverableQueueImpl::getPersistenceId() const { return queue->getPersistenceId(); diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp index 9a84db547c..5d96467bbf 100644 --- a/cpp/src/qpid/broker/SemanticState.cpp +++ b/cpp/src/qpid/broker/SemanticState.cpp @@ -29,7 +29,7 @@ #include "qpid/broker/SessionContext.h" #include "qpid/broker/SessionOutputException.h" #include "qpid/broker/TxAccept.h" -#include "qpid/broker/TxPublish.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/SequenceSet.h" @@ -65,9 +65,8 @@ using qpid::management::Manageable; using qpid::management::Args; namespace _qmf = qmf::org::apache::qpid::broker; -SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss) +SemanticState::SemanticState(SessionState& ss) : session(ss), - deliveryAdapter(da), tagGenerator("sgen"), dtxSelected(false), authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isUserProxyAuth()), @@ -89,7 +88,7 @@ void SemanticState::closed() { if (dtxBuffer.get()) { dtxBuffer->fail(); } - recover(true); + requeue(); //now unsubscribe, which may trigger queue deletion and thus //needs to occur after the requeueing of unacked messages @@ -124,7 +123,7 @@ void SemanticState::consume(const string& tag, resumeId, resumeTtl, arguments); if (!c) // Create plain consumer c = ConsumerImpl::shared_ptr( - new ConsumerImpl(this, name, queue, ackRequired, acquire, exclusive, tag, + new ConsumerImpl(this, name, queue, ackRequired, acquire ? CONSUMER : BROWSER, exclusive, tag, resumeId, resumeTtl, arguments)); queue->consume(c, exclusive);//may throw exception consumers[tag] = c; @@ -281,7 +280,7 @@ SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, const string& _name, Queue::shared_ptr _queue, bool ack, - bool _acquire, + SubscriptionType type, bool _exclusive, const string& _tag, const string& _resumeId, @@ -289,11 +288,11 @@ SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, const framing::FieldTable& _arguments ) : - Consumer(_name, _acquire), +Consumer(_name, type), parent(_parent), queue(_queue), ackExpected(ack), - acquire(_acquire), + acquire(type == CONSUMER), blocked(true), exclusive(_exclusive), resumeId(_resumeId), @@ -340,32 +339,42 @@ OwnershipToken* SemanticState::ConsumerImpl::getSession() return &(parent->session); } -bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg) +bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg) +{ + return deliver(cursor, msg, shared_from_this()); +} +bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg, boost::shared_ptr<Consumer> consumer) { assertClusterSafe(); - allocateCredit(msg.payload); - DeliveryRecord record(msg, msg.queue->shared_from_this(), getTag(), - shared_from_this(), acquire, !ackExpected, credit.isWindowMode(), 0); + allocateCredit(msg); + DeliveryRecord record(cursor, msg.getSequence(), queue, getTag(), + consumer, acquire, !ackExpected, credit.isWindowMode(), amqp_0_10::MessageTransfer::getRequiredCredit(msg)); bool sync = syncFrequency && ++deliveryCount >= syncFrequency; if (sync) deliveryCount = 0;//reset - parent->deliver(record, sync); + const amqp_0_10::MessageTransfer* transfer = dynamic_cast<const amqp_0_10::MessageTransfer*>(&msg.getEncoding()); + + record.setId(parent->session.deliver(*transfer, getTag(), msg.isRedelivered(), msg.getTtl(), msg.getTimestamp(), + ackExpected ? message::ACCEPT_MODE_EXPLICIT : message::ACCEPT_MODE_NONE, + acquire ? message::ACQUIRE_MODE_PRE_ACQUIRED : message::ACQUIRE_MODE_NOT_ACQUIRED, + msg.getAnnotations(), + sync)); if (credit.isWindowMode() || ackExpected || !acquire) { parent->record(record); } if (acquire && !ackExpected) { // auto acquire && auto accept - msg.queue->dequeue(0, msg); + queue->dequeue(0 /*ctxt*/, cursor); record.setEnded(); } if (mgmtObject) { mgmtObject->inc_delivered(); } return true; } -bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message>) +bool SemanticState::ConsumerImpl::filter(const Message&) { return true; } -bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg) +bool SemanticState::ConsumerImpl::accept(const Message& msg) { assertClusterSafe(); // TODO aconway 2009-06-08: if we have byte & message credit but @@ -389,21 +398,21 @@ ostream& operator<<(ostream& o, const ConsumerName& pc) { } } -void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) +void SemanticState::ConsumerImpl::allocateCredit(const Message& msg) { assertClusterSafe(); Credit original = credit; - credit.consume(1, msg->getRequiredCredit()); + credit.consume(1, qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg)); QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this) << ", was " << original << " now " << credit); } -bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg) +bool SemanticState::ConsumerImpl::checkCredit(const Message& msg) { - bool enoughCredit = credit.check(1, msg->getRequiredCredit()); + bool enoughCredit = credit.check(1, qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg)); QPID_LOG(debug, "Subscription " << ConsumerName(*this) << " has " << (enoughCredit ? "sufficient " : "insufficient") - << " credit for message of " << msg->getRequiredCredit() << " bytes: " + << " credit for message of " << qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg) << " bytes: " << credit); return enoughCredit; } @@ -421,7 +430,6 @@ void SemanticState::disable(ConsumerImpl::shared_ptr c) session.getConnection().outputTasks.removeOutputTask(c.get()); } - void SemanticState::cancel(ConsumerImpl::shared_ptr c) { disable(c); @@ -435,49 +443,20 @@ void SemanticState::cancel(ConsumerImpl::shared_ptr c) c->cancel(); } -void SemanticState::handle(intrusive_ptr<Message> msg) { - if (txBuffer.get()) { - TxPublish* deliverable(new TxPublish(msg)); - TxOp::shared_ptr op(deliverable); - route(msg, *deliverable); - txBuffer->enlist(op); - } else { - DeliverableMessage deliverable(msg); - route(msg, deliverable); - if (msg->isContentReleaseRequested()) { - // NOTE: The log messages in this section are used for flow-to-disk testing (which checks the log for the - // presence of these messages). Do not change these without also checking these tests. - if (msg->isContentReleaseBlocked()) { - QPID_LOG(debug, "Message id=\"" << msg->getProperties<MessageProperties>()->getMessageId() << "\"; pid=0x" << - std::hex << msg->getPersistenceId() << std::dec << ": Content release blocked"); - } else { - msg->releaseContent(); - QPID_LOG(debug, "Message id=\"" << msg->getProperties<MessageProperties>()->getMessageId() << "\"; pid=0x" << - std::hex << msg->getPersistenceId() << std::dec << ": Content released"); - } - } - } -} - -namespace +TxBuffer* SemanticState::getTxBuffer() { -const std::string nullstring; + return txBuffer.get(); } -void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { - msg->computeExpiration(getSession().getBroker().getExpiryPolicy()); +void SemanticState::route(Message& msg, Deliverable& strategy) { + msg.computeExpiration(getSession().getBroker().getExpiryPolicy()); - std::string exchangeName = msg->getExchangeName(); - if (!cacheExchange || cacheExchange->getName() != exchangeName - || cacheExchange->isDestroyed()) - { + std::string exchangeName = qpid::broker::amqp_0_10::MessageTransfer::get(msg).getExchangeName(); + if (!cacheExchange || cacheExchange->getName() != exchangeName || cacheExchange->isDestroyed()) cacheExchange = session.getBroker().getExchanges().get(exchangeName); - } - cacheExchange->setProperties(msg); /* verify the userid if specified: */ - std::string id = - msg->hasProperties<MessageProperties>() ? msg->getProperties<MessageProperties>()->getUserId() : nullstring; + std::string id = msg.getUserId(); if (authMsg && !id.empty() && !session.getConnection().isAuthenticatedUser(id)) { QPID_LOG(debug, "authorised user id : " << userID << " but user id in message declared as " << id); @@ -487,9 +466,9 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { AclModule* acl = getSession().getBroker().getAcl(); if (acl && acl->doTransferAcl()) { - if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg->getRoutingKey() )) + if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg.getRoutingKey() )) throw UnauthorizedAccessException(QPID_MSG(userID << " cannot publish to " << - exchangeName << " with routing-key " << msg->getRoutingKey())); + exchangeName << " with routing-key " << msg.getRoutingKey())); } cacheExchange->route(strategy); @@ -501,9 +480,6 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { if (cacheExchange->getAlternate()) { cacheExchange->getAlternate()->route(strategy); } - if (!strategy.delivered) { - msg->destroy(); - } } } @@ -543,28 +519,20 @@ void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery) } } -void SemanticState::recover(bool requeue) +void SemanticState::requeue() { - if(requeue){ - //take copy and clear unacked as requeue may result in redelivery to this session - //which will in turn result in additions to unacked - DeliveryRecords copy = unacked; - unacked.clear(); - for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue)); - }else{ - for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this)); - //unconfirmed messages re redelivered and therefore have their - //id adjusted, confirmed messages are not and so the ordering - //w.r.t id is lost - sort(unacked.begin(), unacked.end()); - } + //take copy and clear unacked as requeue may result in redelivery to this session + //which will in turn result in additions to unacked + DeliveryRecords copy = unacked; + unacked.clear(); + for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue)); getSession().setUnackedCount(unacked.size()); } -void SemanticState::deliver(DeliveryRecord& msg, bool sync) -{ - return deliveryAdapter.deliver(msg, sync); -} + +SessionContext& SemanticState::getSession() { return session; } +const SessionContext& SemanticState::getSession() const { return session; } + const SemanticState::ConsumerImpl::shared_ptr SemanticState::find(const std::string& destination) const { diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h index 15928ce599..67cfe808d0 100644 --- a/cpp/src/qpid/broker/SemanticState.h +++ b/cpp/src/qpid/broker/SemanticState.h @@ -26,7 +26,6 @@ #include "qpid/broker/Consumer.h" #include "qpid/broker/Credit.h" #include "qpid/broker/Deliverable.h" -#include "qpid/broker/DeliveryAdapter.h" #include "qpid/broker/DeliveryRecord.h" #include "qpid/broker/DtxBuffer.h" #include "qpid/broker/DtxManager.h" @@ -34,12 +33,15 @@ #include "qpid/broker/QueueObserver.h" #include "qpid/broker/TxBuffer.h" +#include "qpid/framing/FieldTable.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/SequenceSet.h" #include "qpid/framing/Uuid.h" #include "qpid/sys/AggregateOutput.h" #include "qpid/sys/Mutex.h" #include "qpid/sys/AtomicValue.h" +#include "qpid/broker/AclModule.h" +#include "qpid/management/Manageable.h" #include "qmf/org/apache/qpid/broker/Subscription.h" #include <list> @@ -47,13 +49,15 @@ #include <vector> #include <boost/enable_shared_from_this.hpp> -#include <boost/intrusive_ptr.hpp> #include <boost/cast.hpp> namespace qpid { namespace broker { +class Exchange; +class MessageStore; class SessionContext; +class SessionState; /** * @@ -94,28 +98,28 @@ class SemanticState : private boost::noncopyable { int deliveryCount; qmf::org::apache::qpid::broker::Subscription* mgmtObject; - bool checkCredit(boost::intrusive_ptr<Message>& msg); - void allocateCredit(boost::intrusive_ptr<Message>& msg); + bool checkCredit(const Message& msg); + void allocateCredit(const Message& msg); bool haveCredit(); protected: QPID_BROKER_EXTERN virtual bool doDispatch(); size_t unacked() { return parent->unacked.size(); } + QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&, boost::shared_ptr<Consumer>); public: typedef boost::shared_ptr<ConsumerImpl> shared_ptr; - 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 ConsumerImpl(SemanticState* parent, + const std::string& name, boost::shared_ptr<Queue> queue, + bool ack, SubscriptionType type, bool exclusive, + const std::string& tag, const std::string& resumeId, + uint64_t resumeTtl, const framing::FieldTable& arguments); + QPID_BROKER_EXTERN ~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 bool deliver(const QueueCursor&, const Message&); + QPID_BROKER_EXTERN bool filter(const Message&); + QPID_BROKER_EXTERN bool accept(const Message&); QPID_BROKER_EXTERN void cancel() {} QPID_BROKER_EXTERN void disableNotify(); @@ -153,7 +157,7 @@ class SemanticState : private boost::noncopyable { SemanticState& getParent() { return *parent; } const SemanticState& getParent() const { return *parent; } - void acknowledged(const broker::QueuedMessage&) {} + void acknowledged(const DeliveryRecord&) {} // manageable entry points QPID_BROKER_EXTERN management::ManagementObject* @@ -168,8 +172,7 @@ class SemanticState : private boost::noncopyable { private: typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap; - SessionContext& session; - DeliveryAdapter& deliveryAdapter; + SessionState& session; ConsumerImplMap consumers; NameGenerator tagGenerator; DeliveryRecords unacked; @@ -185,7 +188,6 @@ class SemanticState : private boost::noncopyable { //needed for queue delete events in auto-delete: const std::string connectionId; - void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy); void checkDtxTimeout(); bool complete(DeliveryRecord&); @@ -196,11 +198,11 @@ class SemanticState : private boost::noncopyable { public: - SemanticState(DeliveryAdapter&, SessionContext&); + SemanticState(SessionState&); ~SemanticState(); - SessionContext& getSession() { return session; } - const SessionContext& getSession() const { return session; } + SessionContext& getSession(); + const SessionContext& getSession() const; const ConsumerImpl::shared_ptr find(const std::string& destination) const; bool find(const std::string& destination, ConsumerImpl::shared_ptr&) const; @@ -239,12 +241,12 @@ class SemanticState : private boost::noncopyable { void endDtx(const std::string& xid, bool fail); void suspendDtx(const std::string& xid); void resumeDtx(const std::string& xid); - void recover(bool requeue); - void deliver(DeliveryRecord& message, bool sync); + TxBuffer* getTxBuffer(); + void requeue(); void acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired); void release(DeliveryId first, DeliveryId last, bool setRedelivered); void reject(DeliveryId first, DeliveryId last); - void handle(boost::intrusive_ptr<Message> msg); + void route(Message& msg, Deliverable& strategy); void completed(const framing::SequenceSet& commands); void accepted(const framing::SequenceSet& commands); diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp index ae994a6bd5..c973098020 100644 --- a/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/cpp/src/qpid/broker/SessionAdapter.cpp @@ -264,7 +264,7 @@ QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name) queue->isDurable(), queue->hasExclusiveOwner(), queue->isAutoDelete(), - queue->getSettings(), + queue->getEncodableSettings(), queue->getMessageCount(), queue->getConsumerCount()); } else { @@ -294,19 +294,24 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& queue = getQueue(name); //TODO: check alternate-exchange is as expected } else { + QueueSettings settings(durable, autoDelete); + try { + settings.populate(arguments, settings.storeSettings); + } catch (const qpid::types::Exception& e) { + throw InvalidArgumentException(e.what()); + } + std::pair<Queue::shared_ptr, bool> queue_created = - getBroker().createQueue(name, durable, - autoDelete, + getBroker().createQueue(name, settings, exclusive ? &session : 0, alternateExchange, - arguments, getConnection().getUserId(), getConnection().getUrl()); queue = queue_created.first; assert(queue); if (queue_created.second) { // This is a new queue //handle automatic cleanup: - if (exclusive) { + if (exclusive && queue->setExclusiveOwner(&session)) { exclusiveQueues.push_back(queue); } } else { diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp index 23fa2ee0ca..9888d12be2 100644 --- a/cpp/src/qpid/broker/SessionHandler.cpp +++ b/cpp/src/qpid/broker/SessionHandler.cpp @@ -32,7 +32,7 @@ using namespace std; using namespace qpid::sys; SessionHandler::SessionHandler(Connection& c, ChannelId ch) - : amqp_0_10::SessionHandler(&c.getOutput(), ch), + : qpid::amqp_0_10::SessionHandler(&c.getOutput(), ch), connection(c), proxy(out), clusterOrderProxy(c.getClusterOrderOutput() ? @@ -75,7 +75,7 @@ ConnectionState& SessionHandler::getConnection() { return connection; } const ConnectionState& SessionHandler::getConnection() const { return connection; } void SessionHandler::handleDetach() { - amqp_0_10::SessionHandler::handleDetach(); + qpid::amqp_0_10::SessionHandler::handleDetach(); assert(&connection.getChannel(channel.get()) == this); if (session.get()) connection.getBroker().getSessionManager().detach(session); @@ -125,7 +125,7 @@ void SessionHandler::attached(const std::string& name) { if (session.get()) { session->addManagementObject(); // Delayed from attachAs() - amqp_0_10::SessionHandler::attached(name); + qpid::amqp_0_10::SessionHandler::attached(name); } else { SessionId id(connection.getUserId(), name); SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig(); diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h index ab87cf41a4..21c736fa37 100644 --- a/cpp/src/qpid/broker/SessionHandler.h +++ b/cpp/src/qpid/broker/SessionHandler.h @@ -41,7 +41,7 @@ class SessionState; * receives incoming frames, handles session controls and manages the * association between the channel and a session. */ -class SessionHandler : public amqp_0_10::SessionHandler { +class SessionHandler : public qpid::amqp_0_10::SessionHandler { public: class ErrorListener { public: diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp index cc02d9ec94..88cdf7e03a 100644 --- a/cpp/src/qpid/broker/SessionState.cpp +++ b/cpp/src/qpid/broker/SessionState.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/SessionState.h" #include "qpid/broker/Broker.h" #include "qpid/broker/ConnectionState.h" +#include "qpid/broker/DeliverableMessage.h" #include "qpid/broker/DeliveryRecord.h" #include "qpid/broker/SessionManager.h" #include "qpid/broker/SessionHandler.h" @@ -28,6 +29,7 @@ #include "qpid/framing/AMQContentBody.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/ServerInvoker.h" #include "qpid/log/Statement.h" @@ -55,9 +57,8 @@ SessionState::SessionState( const SessionState::Configuration& config, bool delayManagement) : qpid::SessionState(id, config), broker(b), handler(&h), - semanticState(*this, *this), + semanticState(*this), adapter(semanticState), - msgBuilder(&broker.getStore()), mgmtObject(0), asyncCommandCompleter(new AsyncCommandCompleter(this)) { @@ -208,7 +209,7 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) { if (frame.getBof() && frame.getBos()) //start of frameset msgBuilder.start(id); - intrusive_ptr<Message> msg(msgBuilder.getMessage()); + intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> msg(msgBuilder.getMessage()); msgBuilder.handle(frame); if (frame.getEof() && frame.getEos()) {//end of frameset if (frame.getBof()) { @@ -218,13 +219,16 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) header.setEof(false); msg->getFrames().append(header); } + DeliverableMessage deliverable(Message(msg, msg), semanticState.getTxBuffer()); if (broker.isTimestamping()) - msg->setTimestamp(); - msg->setPublisher(&getConnection()); + deliverable.getMessage().setTimestamp(); + deliverable.getMessage().setPublisher(&getConnection()); + + + IncompleteIngressMsgXfer xfer(this, msg); msg->getIngressCompletion().begin(); - semanticState.handle(msg); + semanticState.route(deliverable.getMessage(), deliverable); msgBuilder.end(); - IncompleteIngressMsgXfer xfer(this, msg); msg->getIngressCompletion().end(xfer); // allows msg to complete xfer } } @@ -294,18 +298,28 @@ void SessionState::handleOut(AMQFrame& frame) { handler->out(frame); } -void SessionState::deliver(DeliveryRecord& msg, bool sync) +DeliveryId SessionState::deliver(const qpid::broker::amqp_0_10::MessageTransfer& message, + const std::string& destination, bool isRedelivered, uint64_t ttl, uint64_t timestamp, + qpid::framing::message::AcceptMode acceptMode, qpid::framing::message::AcquireMode acquireMode, + const qpid::types::Variant::Map& annotations, bool sync) { uint32_t maxFrameSize = getConnection().getFrameMax(); assert(senderGetCommandPoint().offset == 0); SequenceNumber commandId = senderGetCommandPoint().command; - msg.deliver(getProxy().getHandler(), commandId, maxFrameSize); + + framing::AMQFrame method((framing::MessageTransferBody(framing::ProtocolVersion(), destination, acceptMode, acquireMode))); + method.setEof(false); + getProxy().getHandler().handle(method); + message.sendHeader(getProxy().getHandler(), maxFrameSize, isRedelivered, ttl, timestamp, annotations); + message.sendContent(getProxy().getHandler(), maxFrameSize); + assert(senderGetCommandPoint() == SessionPoint(commandId+1, 0)); // Delivery has moved sendPoint. if (sync) { AMQP_ClientProxy::Execution& p(getProxy().getExecution()); Proxy::ScopedSync s(p); p.sync(); } + return commandId; } void SessionState::sendCompletion() { @@ -349,7 +363,6 @@ void SessionState::addPendingExecutionSync() } } - /** factory for creating a reference-counted IncompleteIngressMsgXfer object * which will be attached to a message that will be completed asynchronously. */ @@ -408,10 +421,10 @@ void SessionState::AsyncCommandCompleter::schedule(boost::intrusive_ptr<AsyncCom /** Track an ingress message that is pending completion */ -void SessionState::AsyncCommandCompleter::addPendingMessage(boost::intrusive_ptr<Message> msg) +void SessionState::AsyncCommandCompleter::addPendingMessage(boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> msg) { qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock); - std::pair<SequenceNumber, boost::intrusive_ptr<Message> > item(msg->getCommandId(), msg); + std::pair<SequenceNumber, boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> > item(msg->getCommandId(), msg); bool unique = pendingMsgs.insert(item).second; if (!unique) { assert(false); @@ -430,13 +443,13 @@ void SessionState::AsyncCommandCompleter::deletePendingMessage(SequenceNumber id /** done when an execution.sync arrives */ void SessionState::AsyncCommandCompleter::flushPendingMessages() { - std::map<SequenceNumber, boost::intrusive_ptr<Message> > copy; + std::map<SequenceNumber, boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> > copy; { qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock); pendingMsgs.swap(copy); // we've only tracked these in case a flush is needed, so nuke 'em now. } // drop lock, so it is safe to call "flush()" - for (std::map<SequenceNumber, boost::intrusive_ptr<Message> >::iterator i = copy.begin(); + for (std::map<SequenceNumber, boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> >::iterator i = copy.begin(); i != copy.end(); ++i) { i->second->flush(); } diff --git a/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h index a8ff7feff9..5e3a77d7ed 100644 --- a/cpp/src/qpid/broker/SessionState.h +++ b/cpp/src/qpid/broker/SessionState.h @@ -23,17 +23,18 @@ */ #include "qpid/SessionState.h" +#include "qpid/framing/enum.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/SequenceSet.h" #include "qpid/sys/Time.h" #include "qpid/management/Manageable.h" #include "qmf/org/apache/qpid/broker/Session.h" #include "qpid/broker/SessionAdapter.h" -#include "qpid/broker/DeliveryAdapter.h" #include "qpid/broker/AsyncCompletion.h" #include "qpid/broker/MessageBuilder.h" #include "qpid/broker/SessionContext.h" #include "qpid/broker/SemanticState.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/sys/Monitor.h" #include <boost/noncopyable.hpp> @@ -58,7 +59,6 @@ namespace broker { class Broker; class ConnectionState; -class Message; class SessionHandler; class SessionManager; @@ -68,7 +68,6 @@ class SessionManager; */ class SessionState : public qpid::SessionState, public SessionContext, - public DeliveryAdapter, public management::Manageable, public framing::FrameHandler::InOutHandler { @@ -105,8 +104,10 @@ class SessionState : public qpid::SessionState, void sendCompletion(); - //delivery adapter methods: - void deliver(DeliveryRecord&, bool sync); + DeliveryId deliver(const qpid::broker::amqp_0_10::MessageTransfer& message, + const std::string& destination, bool isRedelivered, uint64_t ttl, uint64_t timestamp, + qpid::framing::message::AcceptMode, qpid::framing::message::AcquireMode, + const qpid::types::Variant::Map& annotations, bool sync); // Manageable entry points management::ManagementObject* GetManagementObject (void) const; @@ -117,7 +118,7 @@ class SessionState : public qpid::SessionState, // Used by cluster to create replica sessions. SemanticState& getSemanticState() { return semanticState; } - boost::intrusive_ptr<Message> getMessageInProgress() { return msgBuilder.getMessage(); } + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> getMessageInProgress() { return msgBuilder.getMessage(); } SessionAdapter& getSessionAdapter() { return adapter; } const SessionId& getSessionId() const { return getId(); } @@ -199,7 +200,7 @@ class SessionState : public qpid::SessionState, // If an ingress message does not require a Sync, we need to // hold a reference to it in case an Execution.Sync command is received and we // have to manually flush the message. - std::map<SequenceNumber, boost::intrusive_ptr<Message> > pendingMsgs; + std::map<SequenceNumber, boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> > pendingMsgs; /** complete all pending commands, runs in IO thread */ void completeCommands(); @@ -212,7 +213,7 @@ class SessionState : public qpid::SessionState, ~AsyncCommandCompleter() {}; /** track a message pending ingress completion */ - void addPendingMessage(boost::intrusive_ptr<Message> m); + void addPendingMessage(boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> m); void deletePendingMessage(SequenceNumber id); void flushPendingMessages(); /** schedule the processing of a completed ingress message.transfer command */ @@ -246,29 +247,29 @@ class SessionState : public qpid::SessionState, { public: IncompleteIngressMsgXfer( SessionState *ss, - boost::intrusive_ptr<Message> m ) + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> m) : AsyncCommandContext(ss, m->getCommandId()), - session(ss), - msg(m), - requiresAccept(m->requiresAccept()), - requiresSync(m->getFrames().getMethod()->isSync()), - pending(false) {} + session(ss), + msg(m), + requiresAccept(m->requiresAccept()), + requiresSync(m->getFrames().getMethod()->isSync()), + pending(false) {} IncompleteIngressMsgXfer( const IncompleteIngressMsgXfer& x ) : AsyncCommandContext(x.session, x.msg->getCommandId()), - session(x.session), - msg(x.msg), - requiresAccept(x.requiresAccept), - requiresSync(x.requiresSync), - pending(x.pending) {} + session(x.session), + msg(x.msg), + requiresAccept(x.requiresAccept), + requiresSync(x.requiresSync), + pending(x.pending) {} - virtual ~IncompleteIngressMsgXfer() {}; + virtual ~IncompleteIngressMsgXfer() {}; virtual void completed(bool); virtual boost::intrusive_ptr<AsyncCompletion::Callback> clone(); private: SessionState *session; // only valid if sync flag in callback is true - boost::intrusive_ptr<Message> msg; + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> msg; bool requiresAccept; bool requiresSync; bool pending; // true if msg saved on pending list... diff --git a/cpp/src/qpid/broker/SimpleMessage.cpp b/cpp/src/qpid/broker/SimpleMessage.cpp index 1239533edf..3a85ca9ae4 100644 --- a/cpp/src/qpid/broker/SimpleMessage.cpp +++ b/cpp/src/qpid/broker/SimpleMessage.cpp @@ -28,6 +28,17 @@ namespace qpid { namespace broker { +SimpleMessage::SimpleMessage() {} + +SimpleMessage::SimpleMessage(const char* msgData, + const uint32_t msgSize, + boost::intrusive_ptr<PersistableMessage> persistentContext) : + m_msg(msgData, static_cast<size_t>(msgSize)), + m_persistentContext(persistentContext) +{} + + +/* SimpleMessage::SimpleMessage(const char* msgData, const uint32_t msgSize) : m_persistenceId(0ULL), @@ -44,24 +55,28 @@ SimpleMessage::SimpleMessage(const char* msgData, m_store(store), m_msgHandle(store ? store->createMessageHandle(this) : MessageHandle()) {} +*/ SimpleMessage::~SimpleMessage() {} +/* const MessageHandle& SimpleMessage::getHandle() const { - return m_msgHandle; + return m_persistentContext.getHandle(); } MessageHandle& SimpleMessage::getHandle() { - return m_msgHandle; + return m_persistentContext.getHandle(); } +*/ uint64_t SimpleMessage::contentSize() const { return static_cast<uint64_t>(m_msg.size()); } +/* void SimpleMessage::setPersistenceId(uint64_t id) const { m_persistenceId = id; @@ -89,12 +104,18 @@ uint32_t SimpleMessage::encodedHeaderSize() const { return 0; } - +*/ bool SimpleMessage::isPersistent() const { - return m_store != 0; + return m_persistentContext.get() != 0; } +boost::intrusive_ptr<PersistableMessage> +SimpleMessage::getPersistentContext() const { + return m_persistentContext; +} + + uint64_t SimpleMessage::getSize() { return m_msg.size(); diff --git a/cpp/src/qpid/broker/SimpleMessage.h b/cpp/src/qpid/broker/SimpleMessage.h index edfaa8d13b..72ede431e0 100644 --- a/cpp/src/qpid/broker/SimpleMessage.h +++ b/cpp/src/qpid/broker/SimpleMessage.h @@ -25,47 +25,51 @@ #define qpid_broker_SimpleMessage_h_ #include "AsyncStore.h" // DataSource -#include "MessageHandle.h" +//#include "MessageHandle.h" #include "PersistableMessage.h" namespace qpid { namespace broker { -class SimpleMessage: public PersistableMessage, - public DataSource +class SimpleMessage: /*public PersistableMessage,*/ + public DataSource, + public RefCounted { public: - SimpleMessage(const char* msgData, - const uint32_t msgSize); + SimpleMessage(); SimpleMessage(const char* msgData, const uint32_t msgSize, - AsyncStore* store); + boost::intrusive_ptr<PersistableMessage> persistentContext); virtual ~SimpleMessage(); - const MessageHandle& getHandle() const; - MessageHandle& getHandle(); +// const MessageHandle& getHandle() const; +// MessageHandle& getHandle(); uint64_t contentSize() const; - // --- Interface Persistable --- - virtual void setPersistenceId(uint64_t id) const; - virtual uint64_t getPersistenceId() const; - virtual void encode(qpid::framing::Buffer& buffer) const; - virtual uint32_t encodedSize() const; +// // --- Interface Persistable --- +// virtual void setPersistenceId(uint64_t id) const; +// virtual uint64_t getPersistenceId() const; +// virtual void encode(qpid::framing::Buffer& buffer) const; +// virtual uint32_t encodedSize() const; +// +// // --- Interface PersistableMessage --- +// virtual void allDequeuesComplete(); +// virtual uint32_t encodedHeaderSize() const; - // --- Interface PersistableMessage --- - virtual void allDequeuesComplete(); - virtual uint32_t encodedHeaderSize() const; - virtual bool isPersistent() const; + // Persistent operations + bool isPersistent() const; + boost::intrusive_ptr<PersistableMessage> getPersistentContext() const; // --- Interface DataSource --- virtual uint64_t getSize(); // <- same as encodedSize()? virtual void write(char* target); private: - mutable uint64_t m_persistenceId; +// mutable uint64_t m_persistenceId; const std::string m_msg; - AsyncStore* m_store; + boost::intrusive_ptr<PersistableMessage> m_persistentContext; +// AsyncStore* m_store; - MessageHandle m_msgHandle; +// MessageHandle m_msgHandle; }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SimpleQueue.cpp b/cpp/src/qpid/broker/SimpleQueue.cpp index 5cd8841f94..e20edc1f92 100644 --- a/cpp/src/qpid/broker/SimpleQueue.cpp +++ b/cpp/src/qpid/broker/SimpleQueue.cpp @@ -171,7 +171,7 @@ SimpleQueue::enqueue(SimpleTxnBuffer* tb, return false; } if (qm->payload()->isPersistent() && m_store) { - qm->payload()->enqueueAsync(shared_from_this(), m_store); + qm->payload()->getPersistentContext()->enqueueAsync(shared_from_this(), m_store); return asyncEnqueue(tb, qm); } return false; @@ -190,7 +190,7 @@ SimpleQueue::dequeue(SimpleTxnBuffer* tb, return false; } if (qm->payload()->isPersistent() && m_store) { - qm->payload()->dequeueAsync(shared_from_this(), m_store); + qm->payload()->getPersistentContext()->dequeueAsync(shared_from_this(), m_store); return asyncDequeue(tb, qm); } return true; @@ -316,7 +316,7 @@ SimpleQueue::asyncEnqueue(SimpleTxnBuffer* tb, boost::shared_ptr<SimpleQueuedMessage> qm) { assert(qm.get()); boost::shared_ptr<QueueAsyncContext> qac(new QueueAsyncContext(shared_from_this(), - qm->payload(), + /*qm->payload(),*/ tb, &handleAsyncEnqueueResult, &m_resultQueue)); @@ -353,7 +353,7 @@ SimpleQueue::asyncDequeue(SimpleTxnBuffer* tb, boost::shared_ptr<SimpleQueuedMessage> qm) { assert(qm.get()); boost::shared_ptr<QueueAsyncContext> qac(new QueueAsyncContext(shared_from_this(), - qm->payload(), + /*qm->payload(),*/ tb, &handleAsyncDequeueResult, &m_resultQueue)); diff --git a/cpp/src/qpid/broker/SimpleQueuedMessage.cpp b/cpp/src/qpid/broker/SimpleQueuedMessage.cpp index 35ac799ecc..f5135902a4 100644 --- a/cpp/src/qpid/broker/SimpleQueuedMessage.cpp +++ b/cpp/src/qpid/broker/SimpleQueuedMessage.cpp @@ -40,7 +40,7 @@ SimpleQueuedMessage::SimpleQueuedMessage(SimpleQueue* q, m_msg(msg) { if (m_queue->getStore()) { - m_enqHandle = q->getStore()->createEnqueueHandle(msg->getHandle(), q->getHandle()); + m_enqHandle = q->getStore()->createEnqueueHandle(msg->getPersistentContext()->getMessageHandle(), q->getHandle()); } } diff --git a/cpp/src/qpid/broker/ThresholdAlerts.cpp b/cpp/src/qpid/broker/ThresholdAlerts.cpp index 3c9e210d4d..9b4e948e4f 100644 --- a/cpp/src/qpid/broker/ThresholdAlerts.cpp +++ b/cpp/src/qpid/broker/ThresholdAlerts.cpp @@ -20,7 +20,8 @@ */ #include "qpid/broker/ThresholdAlerts.h" #include "qpid/broker/Queue.h" -#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/log/Statement.h" #include "qpid/management/ManagementAgent.h" @@ -30,21 +31,20 @@ namespace qpid { namespace broker { namespace { const qmf::org::apache::qpid::broker::EventQueueThresholdExceeded EVENT("dummy", 0, 0); -bool isQMFv2(const boost::intrusive_ptr<Message> message) +bool isQMFv2(const Message& message) { - const qpid::framing::MessageProperties* props = message->getProperties<qpid::framing::MessageProperties>(); + const qpid::framing::MessageProperties* props = qpid::broker::amqp_0_10::MessageTransfer::get(message).getProperties<qpid::framing::MessageProperties>(); return props && props->getAppId() == "qmf2"; } -bool isThresholdEvent(const boost::intrusive_ptr<Message> message) +bool isThresholdEvent(const Message& message) { - if (message->getIsManagementMessage()) { + if (message.getIsManagementMessage()) { //is this a qmf event? if so is it a threshold event? if (isQMFv2(message)) { - const qpid::framing::FieldTable* headers = message->getApplicationHeaders(); - if (headers && headers->getAsString("qmf.content") == "_event") { + if (message.getPropertyAsString("qmf.content") == "_event") { //decode as list - std::string content = message->getFrames().getContent(); + std::string content = qpid::broker::amqp_0_10::MessageTransfer::get(message).getFrames().getContent(); qpid::types::Variant::List list; qpid::amqp_0_10::ListCodec::decode(content, list); if (list.empty() || list.front().getType() != qpid::types::VAR_MAP) return false; @@ -57,7 +57,7 @@ bool isThresholdEvent(const boost::intrusive_ptr<Message> message) } } } else { - std::string content = message->getFrames().getContent(); + std::string content = qpid::broker::amqp_0_10::MessageTransfer::get(message).getFrames().getContent(); qpid::framing::Buffer buffer(const_cast<char*>(content.data()), content.size()); if (buffer.getOctet() == 'A' && buffer.getOctet() == 'M' && buffer.getOctet() == '2' && buffer.getOctet() == 'e') { buffer.getLong();//sequence @@ -83,9 +83,9 @@ ThresholdAlerts::ThresholdAlerts(const std::string& n, repeatInterval(repeat ? repeat*qpid::sys::TIME_SEC : 0), count(0), size(0), lastAlert(qpid::sys::EPOCH) {} -void ThresholdAlerts::enqueued(const QueuedMessage& m) +void ThresholdAlerts::enqueued(const Message& m) { - size += m.payload->contentSize(); + size += m.getContentSize(); ++count; if ((countThreshold && count >= countThreshold) || (sizeThreshold && size >= sizeThreshold)) { if ((repeatInterval == 0 && lastAlert == qpid::sys::EPOCH) @@ -94,7 +94,7 @@ void ThresholdAlerts::enqueued(const QueuedMessage& m) //enqueued on queues; it may even be that this event //causes a message to be enqueued on the queue we are //tracking, and so we need to avoid recursing - if (isThresholdEvent(m.payload)) return; + if (isThresholdEvent(m)) return; lastAlert = qpid::sys::now(); agent.raiseEvent(qmf::org::apache::qpid::broker::EventQueueThresholdExceeded(name, count, size)); QPID_LOG(info, "Threshold event triggered for " << name << ", count=" << count << ", size=" << size); @@ -102,9 +102,9 @@ void ThresholdAlerts::enqueued(const QueuedMessage& m) } } -void ThresholdAlerts::dequeued(const QueuedMessage& m) +void ThresholdAlerts::dequeued(const Message& m) { - size -= m.payload->contentSize(); + size -= m.getContentSize(); --count; if ((countThreshold && count < countThreshold) || (sizeThreshold && size < sizeThreshold)) { lastAlert = qpid::sys::EPOCH; @@ -127,65 +127,14 @@ void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& a } void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::framing::FieldTable& settings, uint16_t limitRatio) - -{ - qpid::types::Variant::Map map; - qpid::amqp_0_10::translate(settings, map); - observe(queue, agent, map, limitRatio); -} - -template <class T> -class Option -{ - public: - Option(const std::string& name, T d) : defaultValue(d) { names.push_back(name); } - void addAlias(const std::string& name) { names.push_back(name); } - T get(const qpid::types::Variant::Map& settings) const - { - T value(defaultValue); - for (std::vector<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) { - if (get(settings, *i, value)) break; - } - return value; - } - private: - std::vector<std::string> names; - T defaultValue; - - bool get(const qpid::types::Variant::Map& settings, const std::string& name, T& value) const - { - qpid::types::Variant::Map::const_iterator i = settings.find(name); - if (i != settings.end()) { - try { - value = (T) i->second; - } catch (const qpid::types::InvalidConversion&) { - QPID_LOG(warning, "Bad value for" << name << ": " << i->second); - } - return true; - } else { - return false; - } - } -}; - -void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::types::Variant::Map& settings, uint16_t limitRatio) - + const QueueSettings& settings, uint16_t limitRatio) { - //Note: aliases are keys defined by java broker - Option<int64_t> repeatInterval("qpid.alert_repeat_gap", 60); - repeatInterval.addAlias("x-qpid-minimum-alert-repeat-gap"); - //If no explicit threshold settings were given use specified //percentage of any limit from the policy. - const QueuePolicy* policy = queue.getPolicy(); - Option<uint32_t> countThreshold("qpid.alert_count", (uint32_t) (policy && limitRatio ? (policy->getMaxCount()*limitRatio/100) : 0)); - countThreshold.addAlias("x-qpid-maximum-message-count"); - Option<uint64_t> sizeThreshold("qpid.alert_size", (uint64_t) (policy && limitRatio ? (policy->getMaxSize()*limitRatio/100) : 0)); - sizeThreshold.addAlias("x-qpid-maximum-message-size"); + uint32_t countThreshold = settings.alertThreshold.hasCount() ? settings.alertThreshold.getCount() : (settings.maxDepth.getCount()*limitRatio/100); + uint32_t sizeThreshold = settings.alertThreshold.hasSize() ? settings.alertThreshold.getSize() : (settings.maxDepth.getSize()*limitRatio/100); - observe(queue, agent, countThreshold.get(settings), sizeThreshold.get(settings), repeatInterval.get(settings)); + observe(queue, agent, countThreshold, sizeThreshold, settings.alertRepeatInterval); } }} diff --git a/cpp/src/qpid/broker/ThresholdAlerts.h b/cpp/src/qpid/broker/ThresholdAlerts.h index 2b4a46b736..4f985522e2 100644 --- a/cpp/src/qpid/broker/ThresholdAlerts.h +++ b/cpp/src/qpid/broker/ThresholdAlerts.h @@ -27,15 +27,13 @@ #include <string> namespace qpid { -namespace framing { -class FieldTable; -} namespace management { class ManagementAgent; } namespace broker { class Queue; +struct QueueSettings; /** * Class to manage generation of QMF alerts when particular thresholds * are breached on a queue. @@ -48,19 +46,17 @@ class ThresholdAlerts : public QueueObserver const uint32_t countThreshold, const uint64_t sizeThreshold, const long repeatInterval); - void enqueued(const QueuedMessage&); - void dequeued(const QueuedMessage&); - void acquired(const QueuedMessage&) {}; - void requeued(const QueuedMessage&) {}; + void enqueued(const Message&); + void dequeued(const Message&); + void acquired(const Message&) {}; + void requeued(const Message&) {}; static void observe(Queue& queue, qpid::management::ManagementAgent& agent, const uint64_t countThreshold, const uint64_t sizeThreshold, const long repeatInterval); static void observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::framing::FieldTable& settings, uint16_t limitRatio); - static void observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::types::Variant::Map& settings, uint16_t limitRatio); + const QueueSettings& settings, uint16_t limitRatio); private: const std::string name; qpid::management::ManagementAgent& agent; diff --git a/cpp/src/qpid/broker/TxAccept.h b/cpp/src/qpid/broker/TxAccept.h index 314a150176..a59e69a85f 100644 --- a/cpp/src/qpid/broker/TxAccept.h +++ b/cpp/src/qpid/broker/TxAccept.h @@ -71,7 +71,6 @@ namespace qpid { virtual void commit() throw(); virtual void rollback() throw(); virtual ~TxAccept(){} - virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } // Used by cluster replication. const framing::SequenceSet& getAcked() const { return acked; } diff --git a/cpp/src/qpid/broker/TxBuffer.cpp b/cpp/src/qpid/broker/TxBuffer.cpp index d92e6ace48..7663cc525f 100644 --- a/cpp/src/qpid/broker/TxBuffer.cpp +++ b/cpp/src/qpid/broker/TxBuffer.cpp @@ -28,7 +28,7 @@ using namespace qpid::broker; bool TxBuffer::prepare(TransactionContext* const ctxt) { - for(op_iterator i = ops.begin(); i < ops.end(); i++){ + for(op_iterator i = ops.begin(); i != ops.end(); i++){ if(!(*i)->prepare(ctxt)){ return false; } @@ -74,7 +74,3 @@ bool TxBuffer::commitLocal(TransactionalStore* const store) } return false; } - -void TxBuffer::accept(TxOpConstVisitor& v) const { - std::for_each(ops.begin(), ops.end(), boost::bind(&TxOp::accept, _1, boost::ref(v))); -} diff --git a/cpp/src/qpid/broker/TxBuffer.h b/cpp/src/qpid/broker/TxBuffer.h index d49c8ba16a..22e2f06be1 100644 --- a/cpp/src/qpid/broker/TxBuffer.h +++ b/cpp/src/qpid/broker/TxBuffer.h @@ -108,9 +108,6 @@ namespace qpid { * commit */ QPID_BROKER_EXTERN bool commitLocal(TransactionalStore* const store); - - // Used by cluster to replicate transaction status. - void accept(TxOpConstVisitor& v) const; }; } } diff --git a/cpp/src/qpid/broker/TxOp.h b/cpp/src/qpid/broker/TxOp.h index a8fa1c2621..775efc92f7 100644 --- a/cpp/src/qpid/broker/TxOp.h +++ b/cpp/src/qpid/broker/TxOp.h @@ -21,7 +21,6 @@ #ifndef _TxOp_ #define _TxOp_ -#include "qpid/broker/TxOpVisitor.h" #include "qpid/broker/TransactionalStore.h" #include <boost/shared_ptr.hpp> @@ -36,8 +35,6 @@ namespace qpid { virtual void commit() throw() = 0; virtual void rollback() throw() = 0; virtual ~TxOp(){} - - virtual void accept(TxOpConstVisitor&) const = 0; }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/TxOpVisitor.h b/cpp/src/qpid/broker/TxOpVisitor.h index ceb894896e..e69de29bb2 100644 --- a/cpp/src/qpid/broker/TxOpVisitor.h +++ b/cpp/src/qpid/broker/TxOpVisitor.h @@ -1,97 +0,0 @@ -#ifndef QPID_BROKER_TXOPVISITOR_H -#define QPID_BROKER_TXOPVISITOR_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -namespace qpid { -namespace broker { - -class DtxAck; -class RecoveredDequeue; -class RecoveredEnqueue; -class TxAccept; -class TxPublish; - -/** - * Visitor for TxOp familly of classes. - */ -struct TxOpConstVisitor -{ - virtual ~TxOpConstVisitor() {} - virtual void operator()(const DtxAck&) = 0; - virtual void operator()(const RecoveredDequeue&) = 0; - virtual void operator()(const RecoveredEnqueue&) = 0; - virtual void operator()(const TxAccept&) = 0; - virtual void operator()(const TxPublish&) = 0; -}; - -}} // namespace qpid::broker - -#endif /*!QPID_BROKER_TXOPVISITOR_H*/ -#ifndef QPID_BROKER_TXOPVISITOR_H -#define QPID_BROKER_TXOPVISITOR_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -namespace qpid { -namespace broker { - -class DtxAck; -class RecoveredDequeue; -class RecoveredEnqueue; -class TxAccept; -class TxPublish; - -/** - * Visitor for TxOp familly of classes. - */ -struct TxOpConstVisitor -{ - virtual ~TxOpConstVisitor() {} - virtual void operator()(const DtxAck&) = 0; - virtual void operator()(const RecoveredDequeue&) = 0; - virtual void operator()(const RecoveredEnqueue&) = 0; - virtual void operator()(const TxAccept&) = 0; - virtual void operator()(const TxPublish&) = 0; -}; - -}} // namespace qpid::broker - -#endif /*!QPID_BROKER_TXOPVISITOR_H*/ diff --git a/cpp/src/qpid/broker/TxPublish.cpp b/cpp/src/qpid/broker/TxPublish.cpp index 9c2cf4a467..e69de29bb2 100644 --- a/cpp/src/qpid/broker/TxPublish.cpp +++ b/cpp/src/qpid/broker/TxPublish.cpp @@ -1,111 +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/log/Statement.h" -#include "qpid/broker/TxPublish.h" -#include "qpid/broker/Queue.h" - -using boost::intrusive_ptr; -using namespace qpid::broker; - -TxPublish::TxPublish(intrusive_ptr<Message> _msg) : msg(_msg) {} - -bool TxPublish::prepare(TransactionContext* ctxt) throw() -{ - try{ - while (!queues.empty()) { - prepare(ctxt, queues.front()); - prepared.push_back(queues.front()); - queues.pop_front(); - } - return true; - }catch(const std::exception& e){ - QPID_LOG(error, "Failed to prepare: " << e.what()); - }catch(...){ - QPID_LOG(error, "Failed to prepare (unknown error)"); - } - return false; -} - -void TxPublish::commit() throw() -{ - try { - for_each(prepared.begin(), prepared.end(), Commit(msg)); - if (msg->isContentReleaseRequested()) { - // NOTE: The log messages in this section are used for flow-to-disk testing (which checks the log for the - // presence of these messages). Do not change these without also checking these tests. - if (msg->isContentReleaseBlocked()) { - QPID_LOG(debug, "Message id=\"" << msg->getProperties<qpid::framing::MessageProperties>()->getMessageId() << "\"; pid=0x" << - std::hex << msg->getPersistenceId() << std::dec << ": Content release blocked on commit"); - } else { - msg->releaseContent(); - QPID_LOG(debug, "Message id=\"" << msg->getProperties<qpid::framing::MessageProperties>()->getMessageId() << "\"; pid=0x" << - std::hex << msg->getPersistenceId() << std::dec << ": Content released on commit"); - } - } - } catch (const std::exception& e) { - QPID_LOG(error, "Failed to commit: " << e.what()); - } catch(...) { - QPID_LOG(error, "Failed to commit (unknown error)"); - } -} - -void TxPublish::rollback() throw() -{ - try { - for_each(prepared.begin(), prepared.end(), Rollback(msg)); - } catch (const std::exception& e) { - QPID_LOG(error, "Failed to complete rollback: " << e.what()); - } catch(...) { - QPID_LOG(error, "Failed to complete rollback (unknown error)"); - } - -} - -void TxPublish::deliverTo(const boost::shared_ptr<Queue>& queue){ - if (!queue->isLocal(msg)) { - queues.push_back(queue); - delivered = true; - } else { - QPID_LOG(debug, "Won't enqueue local message for " << queue->getName()); - } -} - -void TxPublish::prepare(TransactionContext* ctxt, const boost::shared_ptr<Queue> queue) -{ - queue->enqueue(ctxt, msg); -} - -TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){} - -void TxPublish::Commit::operator()(const boost::shared_ptr<Queue>& queue){ - queue->process(msg); -} - -TxPublish::Rollback::Rollback(intrusive_ptr<Message>& _msg) : msg(_msg){} - -void TxPublish::Rollback::operator()(const boost::shared_ptr<Queue>& queue){ - queue->enqueueAborted(msg); -} - -uint64_t TxPublish::contentSize () -{ - return msg->contentSize (); -} diff --git a/cpp/src/qpid/broker/TxPublish.h b/cpp/src/qpid/broker/TxPublish.h index dba7878af2..e69de29bb2 100644 --- a/cpp/src/qpid/broker/TxPublish.h +++ b/cpp/src/qpid/broker/TxPublish.h @@ -1,92 +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 _TxPublish_ -#define _TxPublish_ - -#include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/Deliverable.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/MessageStore.h" -#include "qpid/broker/TxOp.h" - -#include <algorithm> -#include <functional> -#include <list> - -#include <boost/intrusive_ptr.hpp> - -namespace qpid { -namespace broker { -/** - * Defines the behaviour for publish operations on a - * transactional channel. Messages are routed through - * exchanges when received but are not at that stage delivered - * to the matching queues, rather the queues are held in an - * instance of this class. On prepare() the message is marked - * enqueued to the relevant queues in the MessagesStore. On - * commit() the messages will be passed to the queue for - * dispatch or to be added to the in-memory queue. - */ -class QPID_BROKER_CLASS_EXTERN TxPublish : public TxOp, public Deliverable{ - - class Commit{ - boost::intrusive_ptr<Message>& msg; - public: - Commit(boost::intrusive_ptr<Message>& msg); - void operator()(const boost::shared_ptr<Queue>& queue); - }; - class Rollback{ - boost::intrusive_ptr<Message>& msg; - public: - Rollback(boost::intrusive_ptr<Message>& msg); - void operator()(const boost::shared_ptr<Queue>& queue); - }; - - boost::intrusive_ptr<Message> msg; - std::list<boost::shared_ptr<Queue> > queues; - std::list<boost::shared_ptr<Queue> > prepared; - - void prepare(TransactionContext* ctxt, boost::shared_ptr<Queue>); - - public: - QPID_BROKER_EXTERN TxPublish(boost::intrusive_ptr<Message> msg); - QPID_BROKER_EXTERN virtual bool prepare(TransactionContext* ctxt) throw(); - QPID_BROKER_EXTERN virtual void commit() throw(); - QPID_BROKER_EXTERN virtual void rollback() throw(); - - virtual Message& getMessage() { return *msg; }; - - QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue); - - virtual ~TxPublish(){} - virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } - - QPID_BROKER_EXTERN uint64_t contentSize(); - - boost::intrusive_ptr<Message> getMessage() const { return msg; } - const std::list<boost::shared_ptr<Queue> >& getQueues() const { return queues; } - const std::list<boost::shared_ptr<Queue> >& getPrepared() const { return prepared; } -}; -} -} - - -#endif diff --git a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp new file mode 100644 index 0000000000..cac4434c48 --- /dev/null +++ b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp @@ -0,0 +1,373 @@ +/* + * + * 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 "MessageTransfer.h" +#include "qpid/broker/MapHandler.h" +#include "qpid/broker/Message.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/TypeFilter.h" +#include "qpid/framing/SendContent.h" +#include "qpid/log/Statement.h" + +using namespace qpid::framing; + +namespace qpid { +namespace broker { +namespace amqp_0_10 { +namespace { +const std::string QMF2("qmf2"); +const std::string PARTIAL("partial"); +} +MessageTransfer::MessageTransfer() : frames(framing::SequenceNumber()), requiredCredit(0), cachedRequiredCredit(false) {} +MessageTransfer::MessageTransfer(const framing::SequenceNumber& id) : frames(id), requiredCredit(0), cachedRequiredCredit(false) {} + +uint64_t MessageTransfer::getContentSize() const +{ + return frames.getContentSize(); +} + +std::string MessageTransfer::getAnnotationAsString(const std::string& key) const +{ + const qpid::framing::MessageProperties* mp = getProperties<qpid::framing::MessageProperties>(); + if (mp && mp->hasApplicationHeaders()) { + return mp->getApplicationHeaders().getAsString(key); + } else { + return std::string(); + } +} +std::string MessageTransfer::getPropertyAsString(const std::string& key) const { return getAnnotationAsString(key); } + +bool MessageTransfer::getTtl(uint64_t& result) const +{ + const qpid::framing::DeliveryProperties* dp = getProperties<qpid::framing::DeliveryProperties>(); + if (dp && dp->hasTtl()) { + result = dp->getTtl(); + return true; + } else { + return false; + } +} +bool MessageTransfer::hasExpiration() const +{ + const qpid::framing::DeliveryProperties* dp = getProperties<qpid::framing::DeliveryProperties>(); + if (dp && dp->hasExpiration()) { + return true; + } else { + return false; + } +} + +uint8_t MessageTransfer::getPriority() const +{ + const qpid::framing::DeliveryProperties* dp = getProperties<qpid::framing::DeliveryProperties>(); + if (dp && dp->hasPriority()) { + return dp->getPriority(); + } else { + return 0; + } +} + +std::string MessageTransfer::getExchangeName() const +{ + return getFrames().as<framing::MessageTransferBody>()->getDestination(); +} + +bool MessageTransfer::requiresAccept() const +{ + const framing::MessageTransferBody* b = getFrames().as<framing::MessageTransferBody>(); + return b && b->getAcceptMode() == 0/*EXPLICIT == 0*/; +} +uint32_t MessageTransfer::getRequiredCredit() const +{ + if (cachedRequiredCredit) { + return requiredCredit; + } else { + qpid::framing::SumBodySize sum; + frames.map_if(sum, qpid::framing::TypeFilter2<qpid::framing::HEADER_BODY, qpid::framing::CONTENT_BODY>()); + return sum.getSize(); + } +} +void MessageTransfer::computeRequiredCredit() +{ + //add up payload for all header and content frames in the frameset + qpid::framing::SumBodySize sum; + frames.map_if(sum, qpid::framing::TypeFilter2<qpid::framing::HEADER_BODY, qpid::framing::CONTENT_BODY>()); + requiredCredit = sum.getSize(); + cachedRequiredCredit = true; +} +uint32_t MessageTransfer::getRequiredCredit(const qpid::broker::Message& msg) +{ + //TODO: may need to reflect annotations and other modifications in this also + return get(msg).getRequiredCredit(); +} + +qpid::framing::FrameSet& MessageTransfer::getFrames() +{ + return frames; +} +const qpid::framing::FrameSet& MessageTransfer::getFrames() const +{ + return frames; +} +void MessageTransfer::sendContent(framing::FrameHandler& out, uint16_t maxFrameSize) const +{ + qpid::framing::Count c; + frames.map_if(c, qpid::framing::TypeFilter<qpid::framing::CONTENT_BODY>()); + + qpid::framing::SendContent f(out, maxFrameSize, c.getCount()); + frames.map_if(f, qpid::framing::TypeFilter<qpid::framing::CONTENT_BODY>()); +} + +class SendHeader +{ + public: + SendHeader(FrameHandler& h, bool r, uint64_t t, uint64_t ts, const qpid::types::Variant::Map& a) : handler(h), redelivered(r), ttl(t), timestamp(ts), annotations(a) {} + void operator()(const AMQFrame& f) + { + AMQFrame copy = f; + if (redelivered || ttl || timestamp || annotations.size()) { + copy.cloneBody(); + if (annotations.size()) { + MessageProperties* props = + copy.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + for (qpid::types::Variant::Map::const_iterator i = annotations.begin(); + i != annotations.end(); ++i) { + props->getApplicationHeaders().setString(i->first, i->second.asString()); + } + } + if (redelivered || ttl || timestamp) { + DeliveryProperties* dp = + copy.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + if (ttl) dp->setTtl(ttl); + if (redelivered) dp->setRedelivered(redelivered); + if (timestamp) dp->setTimestamp(timestamp); + } + } + handler.handle(copy); + } + private: + FrameHandler& handler; + bool redelivered; + uint64_t ttl; + uint64_t timestamp; + const qpid::types::Variant::Map& annotations; +}; + +void MessageTransfer::sendHeader(framing::FrameHandler& out, uint16_t /*maxFrameSize*/, + bool redelivered, uint64_t ttl, uint64_t timestamp, + const qpid::types::Variant::Map& annotations) const +{ + SendHeader f(out, redelivered, ttl, timestamp, annotations); + frames.map_if(f, TypeFilter<HEADER_BODY>()); +} +bool MessageTransfer::isImmediateDeliveryRequired(const qpid::broker::Message& /*message*/) +{ + return false;//TODO +} + +const framing::SequenceNumber& MessageTransfer::getCommandId() const { return frames.getId(); } + +std::string MessageTransfer::getRoutingKey() const +{ + const qpid::framing::DeliveryProperties* dp = getProperties<qpid::framing::DeliveryProperties>(); + if (dp && dp->hasRoutingKey()) { + return dp->getRoutingKey(); + } else { + return std::string(); + } +} +bool MessageTransfer::isPersistent() const +{ + const qpid::framing::DeliveryProperties* dp = getProperties<qpid::framing::DeliveryProperties>(); + if (dp && dp->hasDeliveryMode()) { + return dp->getDeliveryMode() == 2; + } else { + return false; + } +} + +std::string MessageTransfer::getContent() const +{ + return frames.getContent(); +} + +void MessageTransfer::decodeHeader(framing::Buffer& buffer) +{ + AMQFrame method; + method.decode(buffer); + frames.append(method); + + AMQFrame header; + header.decode(buffer); + frames.append(header); +} +void MessageTransfer::decodeContent(framing::Buffer& buffer) +{ + if (buffer.available()) { + //get the data as a string and set that as the content + //body on a frame then add that frame to the frameset + AMQFrame frame((AMQContentBody())); + frame.castBody<AMQContentBody>()->decode(buffer, buffer.available()); + frame.setFirstSegment(false); + frames.append(frame); + } else { + //adjust header flags + MarkLastSegment f; + frames.map_if(f, TypeFilter<HEADER_BODY>()); + } +} + +void MessageTransfer::encode(framing::Buffer& buffer) const +{ + //encode method and header frames + EncodeFrame f1(buffer); + frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>()); + + //then encode the payload of each content frame + framing::EncodeBody f2(buffer); + frames.map_if(f2, TypeFilter<CONTENT_BODY>()); +} + +void MessageTransfer::encodeContent(framing::Buffer& buffer) const +{ + //encode the payload of each content frame + EncodeBody f2(buffer); + frames.map_if(f2, TypeFilter<CONTENT_BODY>()); +} + +uint32_t MessageTransfer::encodedSize() const +{ + return encodedHeaderSize() + encodedContentSize(); +} + +uint32_t MessageTransfer::encodedContentSize() const +{ + return frames.getContentSize(); +} + +uint32_t MessageTransfer::encodedHeaderSize() const +{ + //add up the size for all method and header frames in the frameset + SumFrameSize sum; + frames.map_if(sum, TypeFilter2<METHOD_BODY, HEADER_BODY>()); + return sum.getSize(); +} + +bool MessageTransfer::isQMFv2() const +{ + const framing::MessageProperties* props = getProperties<framing::MessageProperties>(); + return props && props->getAppId() == QMF2 && props->hasApplicationHeaders(); +} + +bool MessageTransfer::isQMFv2(const qpid::broker::Message& message) +{ + const MessageTransfer* transfer = dynamic_cast<const MessageTransfer*>(&message.getEncoding()); + return transfer && transfer->isQMFv2(); +} + +bool MessageTransfer::isLastQMFResponse(const std::string correlation) const +{ + const framing::MessageProperties* props = getProperties<framing::MessageProperties>(); + return props && props->getCorrelationId() == correlation + && props->hasApplicationHeaders() && !props->getApplicationHeaders().isSet(PARTIAL); +} + +bool MessageTransfer::isLastQMFResponse(const qpid::broker::Message& message, const std::string correlation) +{ + const MessageTransfer* transfer = dynamic_cast<const MessageTransfer*>(&message.getEncoding()); + return transfer && transfer->isLastQMFResponse(correlation); +} + + +void MessageTransfer::processProperties(qpid::broker::MapHandler& handler) const +{ + const qpid::framing::MessageProperties* mp = getProperties<qpid::framing::MessageProperties>(); + if (mp && mp->hasApplicationHeaders()) { + const FieldTable ft = mp->getApplicationHeaders(); + for (FieldTable::const_iterator i = ft.begin(); i != ft.end(); ++i) { + qpid::broker::MapHandler::CharSequence key; + key.data = i->first.data(); + key.size = i->first.size(); + FieldTable::ValuePtr v = i->second; + //TODO: something more sophisticated... + if (v->empty()) { + handler.handleVoid(key); + } else if (v->convertsTo<uint64_t>()) { + handler.handleUint64(key, v->get<uint64_t>()); + } else if (v->convertsTo<int64_t>()) { + handler.handleInt64(key, v->get<int64_t>()); + } else if (v->convertsTo<std::string>()) { + std::string s = v->get<std::string>(); + qpid::broker::MapHandler::CharSequence value; + value.data = s.data(); + value.size = s.size(); + qpid::broker::MapHandler::CharSequence encoding; encoding.size = 0; encoding.data = 0; + handler.handleString(key, value, encoding); + } else { + QPID_LOG(debug, "Unhandled key!" << *v); + } + } + } +} + +std::string MessageTransfer::getUserId() const +{ + const qpid::framing::MessageProperties* mp = getProperties<qpid::framing::MessageProperties>(); + if (mp && mp->hasUserId()) return mp->getUserId(); + else return std::string(); + +} +MessageTransfer::MessageTransfer(const qpid::framing::FrameSet& f) : frames(f), requiredCredit(0) {} + +boost::intrusive_ptr<PersistableMessage> MessageTransfer::merge(const std::map<std::string, qpid::types::Variant>& annotations) const +{ + boost::intrusive_ptr<MessageTransfer> clone(new MessageTransfer(this->frames)); + qpid::framing::MessageProperties* mp = clone->frames.getHeaders()->get<qpid::framing::MessageProperties>(true); + for (qpid::types::Variant::Map::const_iterator i = annotations.begin(); i != annotations.end(); ++i) { + mp->getApplicationHeaders().setString(i->first, i->second); + } + return clone; +} +} +// qpid::broker namespace, TODO: move these elsewhere! +void encode(const Message& in, std::string& out) +{ + const amqp_0_10::MessageTransfer& transfer = amqp_0_10::MessageTransfer::get(in); + uint32_t size = transfer.encodedSize(); + std::vector<char> data(size); + qpid::framing::Buffer buffer(&(data[0]), size); + transfer.encode(buffer); + buffer.reset(); + buffer.getRawData(out, size); +} +void decode(const std::string& in, Message& out) +{ + boost::intrusive_ptr<amqp_0_10::MessageTransfer> transfer(new amqp_0_10::MessageTransfer); + qpid::framing::Buffer buffer(const_cast<char*>(in.data()), in.size()); + transfer->decodeHeader(buffer); + transfer->decodeContent(buffer); + out = Message(transfer, transfer); +} + +}} // namespace qpid::broker::amqp_0_10 diff --git a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h new file mode 100644 index 0000000000..590e389518 --- /dev/null +++ b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h @@ -0,0 +1,133 @@ +#ifndef QPID_BROKER_AMQP_0_10_MESSAGETRANSFER_H +#define QPID_BROKER_AMQP_0_10_MESSAGETRANSFER_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/BrokerImportExport.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/PersistableMessage.h" +#include "qpid/types/Variant.h" + +namespace qpid { +namespace broker { +class Queue; +namespace amqp_0_10 { + +/** + * + */ +class MessageTransfer : public qpid::broker::Message::Encoding, public qpid::broker::PersistableMessage +{ + public: + QPID_BROKER_EXTERN MessageTransfer(); + QPID_BROKER_EXTERN MessageTransfer(const qpid::framing::SequenceNumber&); + + std::string getRoutingKey() const; + bool isPersistent() const; + uint8_t getPriority() const; + uint64_t getContentSize() const; + std::string getPropertyAsString(const std::string& key) const; + std::string getAnnotationAsString(const std::string& key) const; + bool getTtl(uint64_t&) const; + bool hasExpiration() const; + std::string getExchangeName() const; + void processProperties(MapHandler&) const; + std::string getUserId() const; + + bool requiresAccept() const; + const qpid::framing::SequenceNumber& getCommandId() const; + QPID_BROKER_EXTERN qpid::framing::FrameSet& getFrames(); + QPID_BROKER_EXTERN const qpid::framing::FrameSet& getFrames() const; + + template <class T> const T* getProperties() const { + const qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + return p->get<T>(); + } + + template <class T> const T* hasProperties() const { + const qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + return p->get<T>(); + } + template <class T> const T* getMethod() const { + return frames.as<T>(); + } + + template <class T> T* getMethod() { + return frames.as<T>(); + } + + template <class T> bool isA() const { + return frames.isA<T>(); + } + + template <class T> void eraseProperties() { + qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + p->erase<T>(); + } + std::string getContent() const; + uint32_t getRequiredCredit() const; + void computeRequiredCredit(); + + void clearApplicationHeadersFlag(); + void sendContent(framing::FrameHandler& out, uint16_t maxFrameSize) const; + void sendHeader(framing::FrameHandler& out, uint16_t maxFrameSize, bool redelivered, uint64_t ttl, uint64_t timestamp, const qpid::types::Variant::Map& annotations) const; + + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer); + + void encode(framing::Buffer& buffer) const; + uint32_t encodedSize() const; + + /** + * @returns the size of the buffer needed to encode the + * 'header' of this message (not just the header frame, + * but other meta data e.g.routing key and exchange) + */ + uint32_t encodedHeaderSize() const; + boost::intrusive_ptr<PersistableMessage> merge(const std::map<std::string, qpid::types::Variant>& annotations) const; + + QPID_BROKER_EXTERN bool isQMFv2() const; + QPID_BROKER_EXTERN bool isLastQMFResponse(const std::string correlation) const; + + static bool isImmediateDeliveryRequired(const qpid::broker::Message& message); + static uint32_t getRequiredCredit(const qpid::broker::Message&); + static MessageTransfer& get(qpid::broker::Message& message) { + return *dynamic_cast<MessageTransfer*>(&message.getEncoding()); + } + static const MessageTransfer& get(const qpid::broker::Message& message) { + return *dynamic_cast<const MessageTransfer*>(&message.getEncoding()); + } + QPID_BROKER_EXTERN static bool isQMFv2(const qpid::broker::Message& message); + QPID_BROKER_EXTERN static bool isLastQMFResponse(const qpid::broker::Message& message, const std::string correlation); + private: + qpid::framing::FrameSet frames; + uint32_t requiredCredit; + bool cachedRequiredCredit; + + MessageTransfer(const qpid::framing::FrameSet&); + void encodeHeader(framing::Buffer& buffer) const; + uint32_t encodedContentSize() const; + void encodeContent(framing::Buffer& buffer) const; +}; +}}} // namespace qpid::broker::amqp_0_10 + +#endif /*!QPID_BROKER_AMQP_0_10_MESSAGETRANSFER_H*/ diff --git a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp index fb59d058f8..420e04e832 100644 --- a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp +++ b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp @@ -281,7 +281,7 @@ void SslProtocolFactory::established(sys::Poller::shared_ptr poller, boost::bind(&AsynchIOHandler::idle, async, _1)); } - async->init(aio, brokerTimer, maxNegotiateTime, 4); + async->init(aio, brokerTimer, maxNegotiateTime); aio->start(poller); } diff --git a/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp index 4c6fadd28a..c2081a88f2 100644 --- a/cpp/src/qpid/client/SslConnector.cpp +++ b/cpp/src/qpid/client/SslConnector.cpp @@ -38,7 +38,6 @@ #include "qpid/Msg.h" #include <iostream> -#include <map> #include <boost/bind.hpp> #include <boost/format.hpp> @@ -54,53 +53,29 @@ using boost::str; class SslConnector : public Connector { - struct Buff; - - /** Batch up frames for writing to aio. */ - class Writer : public framing::FrameHandler { - typedef sys::ssl::SslIOBufferBase BufferBase; - typedef std::vector<framing::AMQFrame> Frames; - - const uint16_t maxFrameSize; - sys::Mutex lock; - sys::ssl::SslIO* aio; - BufferBase* buffer; - Frames frames; - size_t lastEof; // Position after last EOF in frames - framing::Buffer encode; - size_t framesEncoded; - std::string identifier; - Bounds* bounds; - - void writeOne(); - void newBuffer(); - - public: - - Writer(uint16_t maxFrameSize, Bounds*); - ~Writer(); - void init(std::string id, sys::ssl::SslIO*); - void handle(framing::AMQFrame&); - void write(sys::ssl::SslIO&); - }; + typedef std::deque<framing::AMQFrame> Frames; const uint16_t maxFrameSize; + + sys::Mutex lock; + Frames frames; + size_t lastEof; // Position after last EOF in frames + uint64_t currentSize; + Bounds* bounds; + framing::ProtocolVersion version; bool initiated; - SecuritySettings securitySettings; - - sys::Mutex closedLock; bool closed; sys::ShutdownHandler* shutdownHandler; framing::InputHandler* input; - Writer writer; - sys::ssl::SslSocket socket; sys::ssl::SslIO* aio; + std::string identifier; Poller::shared_ptr poller; + SecuritySettings securitySettings; ~SslConnector(); @@ -110,10 +85,7 @@ class SslConnector : public Connector void eof(qpid::sys::ssl::SslIO&); void disconnected(qpid::sys::ssl::SslIO&); - std::string identifier; - void connect(const std::string& host, const std::string& port); - void init(); void close(); void send(framing::AMQFrame& frame); void abort() {} // TODO: Need to fix for heartbeat timeouts to work @@ -126,17 +98,16 @@ class SslConnector : public Connector const SecuritySettings* getSecuritySettings(); void socketClosed(qpid::sys::ssl::SslIO&, const qpid::sys::ssl::SslSocket&); + size_t decode(const char* buffer, size_t size); + size_t encode(const char* buffer, size_t size); + bool canEncode(); + public: SslConnector(Poller::shared_ptr p, framing::ProtocolVersion pVersion, const ConnectionSettings&, ConnectionImpl*); }; -struct SslConnector::Buff : public SslIO::BufferBase { - Buff(size_t size) : SslIO::BufferBase(new char[size], size) {} - ~Buff() { delete [] bytes;} -}; - // Static constructor which registers connector here namespace { Connector* create(Poller::shared_ptr p, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) { @@ -170,12 +141,14 @@ SslConnector::SslConnector(Poller::shared_ptr p, const ConnectionSettings& settings, ConnectionImpl* cimpl) : maxFrameSize(settings.maxFrameSize), + lastEof(0), + currentSize(0), + bounds(cimpl), version(ver), initiated(false), closed(true), shutdownHandler(0), input(0), - writer(maxFrameSize, cimpl), aio(0), poller(p) { @@ -192,7 +165,7 @@ SslConnector::~SslConnector() { } void SslConnector::connect(const std::string& host, const std::string& port){ - Mutex::ScopedLock l(closedLock); + Mutex::ScopedLock l(lock); assert(closed); try { socket.connect(host, port); @@ -201,7 +174,6 @@ void SslConnector::connect(const std::string& host, const std::string& port){ throw TransportFailure(e.what()); } - identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); closed = false; aio = new SslIO(socket, boost::bind(&SslConnector::readbuff, this, _1, _2), @@ -210,21 +182,16 @@ void SslConnector::connect(const std::string& host, const std::string& port){ boost::bind(&SslConnector::socketClosed, this, _1, _2), 0, // nobuffs boost::bind(&SslConnector::writebuff, this, _1)); - writer.init(identifier, aio); -} -void SslConnector::init(){ - Mutex::ScopedLock l(closedLock); + aio->createBuffers(maxFrameSize); + identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); ProtocolInitiation init(version); writeDataBlock(init); - for (int i = 0; i < 32; i++) { - aio->queueReadBuffer(new Buff(maxFrameSize)); - } aio->start(poller); } void SslConnector::close() { - Mutex::ScopedLock l(closedLock); + Mutex::ScopedLock l(lock); if (!closed) { closed = true; if (aio) @@ -260,76 +227,110 @@ const std::string& SslConnector::getIdentifier() const { } void SslConnector::send(AMQFrame& frame) { - writer.handle(frame); -} - -SslConnector::Writer::Writer(uint16_t s, Bounds* b) : maxFrameSize(s), aio(0), buffer(0), lastEof(0), bounds(b) -{ -} - -SslConnector::Writer::~Writer() { delete buffer; } - -void SslConnector::Writer::init(std::string id, sys::ssl::SslIO* a) { - Mutex::ScopedLock l(lock); - identifier = id; - aio = a; - newBuffer(); -} -void SslConnector::Writer::handle(framing::AMQFrame& frame) { + bool notifyWrite = false; + { Mutex::ScopedLock l(lock); frames.push_back(frame); - if (frame.getEof() || (bounds && bounds->getCurrentSize() >= maxFrameSize)) { + //only ask to write if this is the end of a frameset or if we + //already have a buffers worth of data + currentSize += frame.encodedSize(); + if (frame.getEof()) { lastEof = frames.size(); - aio->notifyPendingWrite(); + notifyWrite = true; + } else { + notifyWrite = (currentSize >= maxFrameSize); + } + /* + NOTE: Moving the following line into this mutex block + is a workaround for BZ 570168, in which the test + testConcurrentSenders causes a hang about 1.5% + of the time. ( To see the hang much more frequently + leave this line out of the mutex block, and put a + small usleep just before it.) + + TODO mgoulish - fix the underlying cause and then + move this call back outside the mutex. + */ + if (notifyWrite && !closed) aio->notifyPendingWrite(); } - QPID_LOG(trace, "SENT [" << identifier << "]: " << frame); } -void SslConnector::Writer::writeOne() { - assert(buffer); - framesEncoded = 0; +void SslConnector::writebuff(SslIO& /*aio*/) +{ + // It's possible to be disconnected and be writable + if (closed) + return; - buffer->dataStart = 0; - buffer->dataCount = encode.getPosition(); - aio->queueWrite(buffer); - newBuffer(); -} + if (!canEncode()) { + return; + } -void SslConnector::Writer::newBuffer() { - buffer = aio->getQueuedBuffer(); - if (!buffer) buffer = new Buff(maxFrameSize); - encode = framing::Buffer(buffer->bytes, buffer->byteCount); - framesEncoded = 0; + SslIO::BufferBase* buffer = aio->getQueuedBuffer(); + if (buffer) { + + size_t encoded = encode(buffer->bytes, buffer->byteCount); + + buffer->dataStart = 0; + buffer->dataCount = encoded; + aio->queueWrite(buffer); + } } // Called in IO thread. -void SslConnector::Writer::write(sys::ssl::SslIO&) { +bool SslConnector::canEncode() +{ Mutex::ScopedLock l(lock); - assert(buffer); + //have at least one full frameset or a whole buffers worth of data + return lastEof || currentSize >= maxFrameSize; +} + +// Called in IO thread. +size_t SslConnector::encode(const char* buffer, size_t size) +{ + framing::Buffer out(const_cast<char*>(buffer), size); size_t bytesWritten(0); - for (size_t i = 0; i < lastEof; ++i) { - AMQFrame& frame = frames[i]; - uint32_t size = frame.encodedSize(); - if (size > encode.available()) writeOne(); - assert(size <= encode.available()); - frame.encode(encode); - ++framesEncoded; - bytesWritten += size; + { + Mutex::ScopedLock l(lock); + while (!frames.empty() && out.available() >= frames.front().encodedSize() ) { + frames.front().encode(out); + QPID_LOG(trace, "SENT [" << identifier << "]: " << frames.front()); + frames.pop_front(); + if (lastEof) --lastEof; + } + bytesWritten = size - out.available(); + currentSize -= bytesWritten; } - frames.erase(frames.begin(), frames.begin()+lastEof); - lastEof = 0; if (bounds) bounds->reduce(bytesWritten); - if (encode.getPosition() > 0) writeOne(); + return bytesWritten; } -void SslConnector::readbuff(SslIO& aio, SslIO::BufferBase* buff) { - framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); +void SslConnector::readbuff(SslIO& aio, SslIO::BufferBase* buff) +{ + int32_t decoded = decode(buff->bytes+buff->dataStart, buff->dataCount); + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (decoded < buff->dataCount) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += decoded; + buff->dataCount -= decoded; + aio.unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio.queueReadBuffer(buff); + } +} +size_t SslConnector::decode(const char* buffer, size_t size) +{ + framing::Buffer in(const_cast<char*>(buffer), size); if (!initiated) { framing::ProtocolInitiation protocolInit; if (protocolInit.decode(in)) { - //TODO: check the version is correct QPID_LOG(debug, "RECV [" << identifier << "]: INIT(" << protocolInit << ")"); + if(!(protocolInit==version)){ + throw Exception(QPID_MSG("Unsupported version: " << protocolInit + << " supported version " << version)); + } } initiated = true; } @@ -338,25 +339,12 @@ void SslConnector::readbuff(SslIO& aio, SslIO::BufferBase* buff) { QPID_LOG(trace, "RECV [" << identifier << "]: " << frame); input->received(frame); } - // TODO: unreading needs to go away, and when we can cope - // with multiple sub-buffers in the general buffer scheme, it will - if (in.available() != 0) { - // Adjust buffer for used bytes and then "unread them" - buff->dataStart += buff->dataCount-in.available(); - buff->dataCount = in.available(); - aio.unread(buff); - } else { - // Give whole buffer back to aio subsystem - aio.queueReadBuffer(buff); - } -} - -void SslConnector::writebuff(SslIO& aio_) { - writer.write(aio_); + return size - in.available(); } void SslConnector::writeDataBlock(const AMQDataBlock& data) { - SslIO::BufferBase* buff = new Buff(maxFrameSize); + SslIO::BufferBase* buff = aio->getQueuedBuffer(); + assert(buff); framing::Buffer out(buff->bytes, buff->byteCount); data.encode(out); buff->dataCount = data.encodedSize(); diff --git a/cpp/src/qpid/client/SubscriptionManagerImpl.cpp b/cpp/src/qpid/client/SubscriptionManagerImpl.cpp index 7dead112e5..44f81776c0 100644 --- a/cpp/src/qpid/client/SubscriptionManagerImpl.cpp +++ b/cpp/src/qpid/client/SubscriptionManagerImpl.cpp @@ -28,6 +28,7 @@ #include <qpid/client/Session.h> #include <qpid/client/MessageListener.h> #include <qpid/framing/Uuid.h> +#include <qpid/log/Statement.h> #include <set> #include <sstream> @@ -167,6 +168,15 @@ void SubscriptionManagerImpl::setFlowControl(const std::string& name, uint32_t m setFlowControl(name, FlowControl(messages, bytes, window)); } +AutoCancel::AutoCancel(SubscriptionManager& sm_, const std::string& tag_) : sm(sm_), tag(tag_) {} +AutoCancel::~AutoCancel() { + try { + sm.cancel(tag); + } catch (const qpid::Exception& e) { + QPID_LOG(info, "Exception in AutoCancel destructor: " << e.what()); + } +} + }} // namespace qpid::client diff --git a/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp index 1dd951d339..a5c6465bad 100644 --- a/cpp/src/qpid/client/TCPConnector.cpp +++ b/cpp/src/qpid/client/TCPConnector.cpp @@ -46,11 +46,6 @@ using namespace qpid::framing; using boost::format; using boost::str; -struct TCPConnector::Buff : public AsynchIO::BufferBase { - Buff(size_t size) : AsynchIO::BufferBase(new char[size], size) {} - ~Buff() { delete [] bytes;} -}; - // Static constructor which registers connector here namespace { Connector* create(Poller::shared_ptr p, framing::ProtocolVersion v, const ConnectionSettings& s, ConnectionImpl* c) { @@ -118,9 +113,8 @@ void TCPConnector::connected(const Socket&) { void TCPConnector::start(sys::AsynchIO* aio_) { aio = aio_; - for (int i = 0; i < 4; i++) { - aio->queueReadBuffer(new Buff(maxFrameSize)); - } + + aio->createBuffers(maxFrameSize); identifier = str(format("[%1%]") % socket.getFullAddress()); } @@ -226,15 +220,19 @@ void TCPConnector::writebuff(AsynchIO& /*aio*/) return; Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this; - if (codec->canEncode()) { - std::auto_ptr<AsynchIO::BufferBase> buffer = std::auto_ptr<AsynchIO::BufferBase>(aio->getQueuedBuffer()); - if (!buffer.get()) buffer = std::auto_ptr<AsynchIO::BufferBase>(new Buff(maxFrameSize)); + + if (!codec->canEncode()) { + return; + } + + AsynchIO::BufferBase* buffer = aio->getQueuedBuffer(); + if (buffer) { size_t encoded = codec->encode(buffer->bytes, buffer->byteCount); buffer->dataStart = 0; buffer->dataCount = encoded; - aio->queueWrite(buffer.release()); + aio->queueWrite(buffer); } } @@ -307,6 +305,7 @@ size_t TCPConnector::decode(const char* buffer, size_t size) void TCPConnector::writeDataBlock(const AMQDataBlock& data) { AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + assert(buff); framing::Buffer out(buff->bytes, buff->byteCount); data.encode(out); buff->dataCount = data.encodedSize(); diff --git a/cpp/src/qpid/client/TCPConnector.h b/cpp/src/qpid/client/TCPConnector.h index c87d544816..c0bc26028d 100644 --- a/cpp/src/qpid/client/TCPConnector.h +++ b/cpp/src/qpid/client/TCPConnector.h @@ -50,7 +50,6 @@ namespace client { class TCPConnector : public Connector, public sys::Codec { typedef std::deque<framing::AMQFrame> Frames; - struct Buff; const uint16_t maxFrameSize; diff --git a/cpp/src/qpid/cluster/Cluster.cpp b/cpp/src/qpid/cluster/Cluster.cpp deleted file mode 100644 index 34aaf3d341..0000000000 --- a/cpp/src/qpid/cluster/Cluster.cpp +++ /dev/null @@ -1,1294 +0,0 @@ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * <h1>CLUSTER IMPLEMENTATION OVERVIEW</h1> - * - * The cluster works on the principle that if all members of the - * cluster receive identical input, they will all produce identical - * results. cluster::Connections intercept data received from clients - * and multicast it via CPG. The data is processed (passed to the - * broker::Connection) only when it is received from CPG in cluster - * order. Each cluster member has Connection objects for directly - * connected clients and "shadow" Connection objects for connections - * to other members. - * - * This assumes that all broker actions occur deterministically in - * response to data arriving on client connections. There are two - * situations where this assumption fails: - * - sending data in response to polling local connections for writabiliy. - * - taking actions based on a timer or timestamp comparison. - * - * IMPORTANT NOTE: any time code is added to the broker that uses timers, - * the cluster may need to be updated to take account of this. - * - * - * USE OF TIMESTAMPS IN THE BROKER - * - * The following are the current areas where broker uses timers or timestamps: - * - * - Producer flow control: broker::SemanticState uses - * connection::getClusterOrderOutput. a FrameHandler that sends - * frames to the client via the cluster. Used by broker::SessionState - * - * - QueueCleaner, Message TTL: uses ExpiryPolicy, which is - * implemented by cluster::ExpiryPolicy. - * - * - Connection heartbeat: sends connection controls, not part of - * session command counting so OK to ignore. - * - * - LinkRegistry: only cluster elder is ever active for links. - * - * - management::ManagementBroker: uses MessageHandler supplied by cluster - * to send messages to the broker via the cluster. - * - * cluster::ExpiryPolicy uses cluster time. - * - * ClusterTimer implements periodic timed events in the cluster context. - * Used for: - * - periodic management events. - * - DTX transaction timeouts. - * - * <h1>CLUSTER PROTOCOL OVERVIEW</h1> - * - * Messages sent to/from CPG are called Events. - * - * An Event carries a ConnectionId, which includes a MemberId and a - * connection number. - * - * Events are either - * - Connection events: non-0 connection number and are associated with a connection. - * - Cluster Events: 0 connection number, are not associated with a connection. - * - * Events are further categorized as: - * - Control: carries method frame(s) that affect cluster behavior. - * - Data: carries raw data received from a client connection. - * - * The cluster defines extensions to the AMQP command set in ../../../xml/cluster.xml - * which defines two classes: - * - cluster: cluster control information. - * - cluster.connection: control information for a specific connection. - * - * The following combinations are legal: - * - Data frames carrying connection data. - * - Cluster control events carrying cluster commands. - * - Connection control events carrying cluster.connection commands. - * - Connection control events carrying non-cluster frames: frames sent to the client. - * e.g. flow-control frames generated on a timer. - * - * <h1>CLUSTER INITIALIZATION OVERVIEW</h1> - * - * @see InitialStatusMap - * - * When a new member joins the CPG group, all members (including the - * new one) multicast their "initial status." The new member is in - * PRE_INIT mode until it gets a complete set of initial status - * messages from all cluster members. In a newly-forming cluster is - * then in INIT mode until the configured cluster-size members have - * joined. - * - * The newcomer uses initial status to determine - * - The cluster UUID - * - Am I speaking the correct version of the cluster protocol? - * - Do I need to get an update from an existing active member? - * - Can I recover from my own store? - * - * Pre-initialization happens in the Cluster constructor (plugin - * early-init phase) because it needs to set the recovery flag before - * the store initializes. This phase lasts until inital-status is - * received for all active members. The PollableQueues and Multicaster - * are in "bypass" mode during this phase since the poller has not - * started so there are no threads to serve pollable queues. - * - * The remaining initialization happens in Cluster::initialize() or, - * if cluster-size=N is specified, in the deliver thread when an - * initial-status control is delivered that brings the total to N. - */ -#include "qpid/Exception.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/sys/ClusterSafe.h" -#include "qpid/cluster/ClusterSettings.h" -#include "qpid/cluster/Connection.h" -#include "qpid/cluster/UpdateClient.h" -#include "qpid/cluster/RetractClient.h" -#include "qpid/cluster/FailoverExchange.h" -#include "qpid/cluster/UpdateDataExchange.h" -#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" -#include "qmf/org/apache/qpid/cluster/Package.h" -#include "qpid/broker/Broker.h" -#include "qpid/broker/Connection.h" -#include "qpid/broker/NullMessageStore.h" -#include "qpid/broker/QueueRegistry.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/SessionState.h" -#include "qpid/broker/SignalHandler.h" -#include "qpid/framing/AMQFrame.h" -#include "qpid/framing/AMQP_AllOperations.h" -#include "qpid/framing/AllInvoker.h" -#include "qpid/framing/ClusterConfigChangeBody.h" -#include "qpid/framing/ClusterClockBody.h" -#include "qpid/framing/ClusterConnectionDeliverCloseBody.h" -#include "qpid/framing/ClusterConnectionAbortBody.h" -#include "qpid/framing/ClusterRetractOfferBody.h" -#include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h" -#include "qpid/framing/ClusterReadyBody.h" -#include "qpid/framing/ClusterShutdownBody.h" -#include "qpid/framing/ClusterUpdateOfferBody.h" -#include "qpid/framing/ClusterUpdateRequestBody.h" -#include "qpid/framing/ClusterConnectionAnnounceBody.h" -#include "qpid/framing/ClusterErrorCheckBody.h" -#include "qpid/framing/ClusterTimerWakeupBody.h" -#include "qpid/framing/ClusterDeliverToQueueBody.h" -#include "qpid/framing/MessageTransferBody.h" -#include "qpid/log/Helpers.h" -#include "qpid/log/Statement.h" -#include "qpid/UrlArray.h" -#include "qpid/management/ManagementAgent.h" -#include "qpid/memory.h" -#include "qpid/sys/Thread.h" - -#include <boost/shared_ptr.hpp> -#include <boost/bind.hpp> -#include <boost/cast.hpp> -#include <boost/current_function.hpp> -#include <algorithm> -#include <iterator> -#include <map> -#include <ostream> - - -namespace qpid { -namespace cluster { -using namespace qpid; -using namespace qpid::framing; -using namespace qpid::sys; -using namespace qpid::cluster; -using namespace framing::cluster; -using namespace std; -using management::ManagementAgent; -using management::ManagementObject; -using management::Manageable; -using management::Args; -namespace _qmf = ::qmf::org::apache::qpid::cluster; -namespace arg=client::arg; - -/** - * NOTE: must increment this number whenever any incompatible changes in - * cluster protocol/behavior are made. It allows early detection and - * sensible reporting of an attempt to mix different versions in a - * cluster. - * - * Currently use SVN revision to avoid clashes with versions from - * different branches. - */ -const uint32_t Cluster::CLUSTER_VERSION = 1332342; - -struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler { - qpid::cluster::Cluster& cluster; - MemberId member; - Cluster::Lock& l; - ClusterDispatcher(Cluster& c, const MemberId& id, Cluster::Lock& l_) : cluster(c), member(id), l(l_) {} - - void updateRequest(const std::string& url) { cluster.updateRequest(member, url, l); } - - void initialStatus(uint32_t version, bool active, const Uuid& clusterId, - uint8_t storeState, const Uuid& shutdownId, - const std::string& firstConfig, const framing::Array& urls) - { - cluster.initialStatus( - member, version, active, clusterId, - framing::cluster::StoreState(storeState), shutdownId, - firstConfig, urls, l); - } - void ready(const std::string& url) { - cluster.ready(member, url, l); - } - void configChange(const std::string& members, - const std::string& left, - const std::string& joined) - { - cluster.configChange(member, members, left, joined, l); - } - void updateOffer(uint64_t updatee) { - cluster.updateOffer(member, updatee, l); - } - void retractOffer(uint64_t updatee) { cluster.retractOffer(member, updatee, l); } - void errorCheck(uint8_t type, const framing::SequenceNumber& frameSeq) { - cluster.errorCheck(member, type, frameSeq, l); - } - void timerWakeup(const std::string& name) { cluster.timerWakeup(member, name, l); } - void timerDrop(const std::string& name) { cluster.timerDrop(member, name, l); } - void shutdown(const Uuid& id) { cluster.shutdown(member, id, l); } - void deliverToQueue(const std::string& queue, const std::string& message) { - cluster.deliverToQueue(queue, message, l); - } - void clock(uint64_t time) { cluster.clock(time, l); } - bool invoke(AMQBody& body) { return framing::invoke(*this, body).wasHandled(); } -}; - -Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : - settings(set), - broker(b), - mgmtObject(0), - poller(b.getPoller()), - cpg(*this), - name(settings.name), - self(cpg.self()), - clusterId(true), - mAgent(0), - expiryPolicy(new ExpiryPolicy(*this)), - mcast(cpg, poller, boost::bind(&Cluster::leave, this)), - dispatcher(cpg, poller, boost::bind(&Cluster::leave, this)), - deliverEventQueue(boost::bind(&Cluster::deliveredEvent, this, _1), - boost::bind(&Cluster::leave, this), - "Error decoding events, may indicate a broker version mismatch", - poller), - deliverFrameQueue(boost::bind(&Cluster::deliveredFrame, this, _1), - boost::bind(&Cluster::leave, this), - "Error delivering frames", - poller), - failoverExchange(new FailoverExchange(broker.GetVhostObject(), &broker)), - credentialsExchange(new CredentialsExchange(*this)), - quorum(boost::bind(&Cluster::leave, this)), - decoder(boost::bind(&Cluster::deliverFrame, this, _1)), - discarding(true), - state(PRE_INIT), - initMap(self, settings.size), - store(broker.getDataDir().getPath()), - elder(false), - lastAliveCount(0), - lastBroker(false), - updateRetracted(false), - updateClosed(false), - error(*this), - acl(0) -{ - broker.setInCluster(true); - - // We give ownership of the timer to the broker and keep a plain pointer. - // This is OK as it means the timer has the same lifetime as the broker. - timer = new ClusterTimer(*this); - broker.setClusterTimer(std::auto_ptr<sys::Timer>(timer)); - - // Failover exchange provides membership updates to clients. - broker.getExchanges().registerExchange(failoverExchange); - - // CredentialsExchange is used to authenticate new cluster members - broker.getExchanges().registerExchange(credentialsExchange); - - // Load my store status before we go into initialization - if (! broker::NullMessageStore::isNullStore(&broker.getStore())) { - store.load(); - clusterId = store.getClusterId(); - QPID_LOG(notice, "Cluster store state: " << store) - } - cpg.join(name); - // pump the CPG dispatch manually till we get past PRE_INIT. - while (state == PRE_INIT) - cpg.dispatchOne(); -} - -Cluster::~Cluster() { - broker.setClusterTimer(std::auto_ptr<sys::Timer>(0)); // Delete cluster timer - if (updateThread) updateThread.join(); // Join the previous updatethread. -} - -void Cluster::initialize() { - if (settings.quorum) quorum.start(poller); - if (settings.url.empty()) - myUrl = Url::getIpAddressesUrl(broker.getPort(broker::Broker::TCP_TRANSPORT)); - else - myUrl = settings.url; - broker.getKnownBrokers = boost::bind(&Cluster::getUrls, this); - broker.deferDelivery = boost::bind(&Cluster::deferDeliveryImpl, this, _1, _2); - broker.setExpiryPolicy(expiryPolicy); - deliverEventQueue.bypassOff(); - deliverEventQueue.start(); - deliverFrameQueue.bypassOff(); - deliverFrameQueue.start(); - mcast.start(); - - /// Create management object - mAgent = broker.getManagementAgent(); - if (mAgent != 0){ - _qmf::Package packageInit(mAgent); - mgmtObject = new _qmf::Cluster (mAgent, this, &broker,name,myUrl.str()); - mAgent->addObject (mgmtObject); - } - - // Run initMapCompleted immediately to process the initial configuration - // that allowed us to transition out of PRE_INIT - assert(state == INIT); - initMapCompleted(*(Mutex::ScopedLock*)0); // Fake lock, single-threaded context. - - // Add finalizer last for exception safety. - broker.addFinalizer(boost::bind(&Cluster::brokerShutdown, this)); - - // Start dispatching CPG events. - dispatcher.start(); -} - -// Called in connection thread to insert a client connection. -void Cluster::addLocalConnection(const boost::intrusive_ptr<Connection>& c) { - assert(c->getId().getMember() == self); - localConnections.insert(c); -} - -// Called in connection thread to insert an updated shadow connection. -void Cluster::addShadowConnection(const boost::intrusive_ptr<Connection>& c) { - QPID_LOG(debug, *this << " new shadow connection " << c->getId()); - // Safe to use connections here because we're pre-catchup, stalled - // and discarding, so deliveredFrame is not processing any - // connection events. - assert(discarding); - pair<ConnectionMap::iterator, bool> ib - = connections.insert(ConnectionMap::value_type(c->getId(), c)); - // Like this to avoid tripping up unused variable warning when NDEBUG set - if (!ib.second) assert(ib.second); -} - -void Cluster::erase(const ConnectionId& id) { - Lock l(lock); - erase(id,l); -} - -void Cluster::eraseLocal(const ConnectionId& id) { - Lock l(lock); - eraseLocal(id,l); -} - -// Called by Connection::deliverClose() in deliverFrameQueue thread. -void Cluster::erase(const ConnectionId& id, Lock&) { - connections.erase(id); - decoder.erase(id); -} - -void Cluster::eraseLocal(const ConnectionId& id, Lock&) { - localConnections.getErase(id); -} - -std::vector<string> Cluster::getIds() const { - Lock l(lock); - return getIds(l); -} - -std::vector<string> Cluster::getIds(Lock&) const { - return map.memberIds(); -} - -std::vector<Url> Cluster::getUrls() const { - Lock l(lock); - return getUrls(l); -} - -std::vector<Url> Cluster::getUrls(Lock&) const { - return map.memberUrls(); -} - -void Cluster::leave() { - Lock l(lock); - leave(l); -} - -#define LEAVE_TRY(STMT) try { STMT; } \ - catch (const std::exception& e) { \ - QPID_LOG(warning, *this << " error leaving cluster: " << e.what()); \ - } do {} while(0) - -void Cluster::leave(Lock&) { - if (state != LEFT) { - state = LEFT; - QPID_LOG(notice, *this << " leaving cluster " << name); - // Finalize connections now now to avoid problems later in destructor. - ClusterSafeScope css; // Don't trigger cluster-safe assertions. - LEAVE_TRY(localConnections.clear()); - LEAVE_TRY(connections.clear()); - LEAVE_TRY(broker::SignalHandler::shutdown()); - } -} - -// Deliver CPG message. -void Cluster::deliver( - cpg_handle_t /*handle*/, - const cpg_name* /*group*/, - uint32_t nodeid, - uint32_t pid, - void* msg, - int msg_len) -{ - MemberId from(nodeid, pid); - framing::Buffer buf(static_cast<char*>(msg), msg_len); - Event e(Event::decodeCopy(from, buf)); - deliverEvent(e); -} - -void Cluster::deliverEvent(const Event& e) { deliverEventQueue.push(e); } - -void Cluster::deliverFrame(const EventFrame& e) { deliverFrameQueue.push(e); } - -const ClusterUpdateOfferBody* castUpdateOffer(const framing::AMQBody* body) { - return (body && body->getMethod() && - body->getMethod()->isA<ClusterUpdateOfferBody>()) ? - static_cast<const ClusterUpdateOfferBody*>(body) : 0; -} - -const ClusterConnectionAnnounceBody* castAnnounce( const framing::AMQBody *body) { - return (body && body->getMethod() && - body->getMethod()->isA<ClusterConnectionAnnounceBody>()) ? - static_cast<const ClusterConnectionAnnounceBody*>(body) : 0; -} - -// Handler for deliverEventQueue. -// This thread decodes frames from events. -void Cluster::deliveredEvent(const Event& e) { - if (e.isCluster()) { - EventFrame ef(e, e.getFrame()); - // Stop the deliverEventQueue on update offers. - // This preserves the connection decoder fragments for an update. - // Only do this for the two brokers that are directly involved in this - // offer: the one making the offer, or the one receiving it. - const ClusterUpdateOfferBody* offer = castUpdateOffer(ef.frame.getBody()); - if (offer && ( e.getMemberId() == self || MemberId(offer->getUpdatee()) == self) ) { - QPID_LOG(info, *this << " stall for update offer from " << e.getMemberId() - << " to " << MemberId(offer->getUpdatee())); - deliverEventQueue.stop(); - } - deliverFrame(ef); - } - else if(!discarding) { - if (e.isControl()) - deliverFrame(EventFrame(e, e.getFrame())); - else { - try { decoder.decode(e, e.getData()); } - catch (const Exception& ex) { - // Close a connection that is sending us invalid data. - QPID_LOG(error, *this << " aborting connection " - << e.getConnectionId() << ": " << ex.what()); - framing::AMQFrame abort((ClusterConnectionAbortBody())); - deliverFrame(EventFrame(EventHeader(CONTROL, e.getConnectionId()), abort)); - } - } - } -} - -void Cluster::flagError( - Connection& connection, ErrorCheck::ErrorType type, const std::string& msg) -{ - Mutex::ScopedLock l(lock); - if (connection.isCatchUp()) { - QPID_LOG(critical, *this << " error on update connection " << connection - << ": " << msg); - leave(l); - } - error.error(connection, type, map.getFrameSeq(), map.getMembers(), msg); -} - -// Handler for deliverFrameQueue. -// This thread executes the main logic. -void Cluster::deliveredFrame(const EventFrame& efConst) { - Mutex::ScopedLock l(lock); - sys::ClusterSafeScope css; // Don't trigger cluster-safe asserts. - if (state == LEFT) return; - EventFrame e(efConst); - const ClusterUpdateOfferBody* offer = castUpdateOffer(e.frame.getBody()); - if (offer && error.isUnresolved()) { - // We can't honour an update offer that is delivered while an - // error is in progress so replace it with a retractOffer and re-start - // the event queue. - e.frame = AMQFrame( - ClusterRetractOfferBody(ProtocolVersion(), offer->getUpdatee())); - deliverEventQueue.start(); - } - // Process each frame through the error checker. - if (error.isUnresolved()) { - error.delivered(e); - while (error.canProcess()) // There is a frame ready to process. - processFrame(error.getNext(), l); - } - else - processFrame(e, l); -} - - -void Cluster::processFrame(const EventFrame& e, Lock& l) { - if (e.isCluster()) { - QPID_LOG_IF(trace, loggable(e.frame), *this << " DLVR: " << e); - ClusterDispatcher dispatch(*this, e.connectionId.getMember(), l); - if (!framing::invoke(dispatch, *e.frame.getBody()).wasHandled()) - throw Exception(QPID_MSG("Invalid cluster control")); - } - else if (state >= CATCHUP) { - map.incrementFrameSeq(); - ConnectionPtr connection = getConnection(e, l); - if (connection) { - QPID_LOG_IF(trace, loggable(e.frame), - *this << " DLVR " << map.getFrameSeq() << ": " << e); - connection->deliveredFrame(e); - } - else - throw Exception(QPID_MSG("Unknown connection: " << e)); - } - else // Drop connection frames while state < CATCHUP - QPID_LOG_IF(trace, loggable(e.frame), *this << " DROP (joining): " << e); -} - -// Called in deliverFrameQueue thread -ConnectionPtr Cluster::getConnection(const EventFrame& e, Lock&) { - ConnectionId id = e.connectionId; - ConnectionMap::iterator i = connections.find(id); - if (i != connections.end()) return i->second; - ConnectionPtr cp; - // If the frame is an announcement for a new connection, add it. - const ClusterConnectionAnnounceBody *announce = castAnnounce(e.frame.getBody()); - if (e.frame.getBody() && e.frame.getMethod() && announce) - { - if (id.getMember() == self) { // Announces one of my own - cp = localConnections.getErase(id); - assert(cp); - } - else { // New remote connection, create a shadow. - qpid::sys::SecuritySettings secSettings; - if (announce) { - secSettings.ssf = announce->getSsf(); - secSettings.authid = announce->getAuthid(); - secSettings.nodict = announce->getNodict(); - } - cp = new Connection(*this, shadowOut, announce->getManagementId(), id, secSettings); - } - connections.insert(ConnectionMap::value_type(id, cp)); - } - return cp; -} - -Cluster::ConnectionVector Cluster::getConnections(Lock&) { - ConnectionVector result(connections.size()); - std::transform(connections.begin(), connections.end(), result.begin(), - boost::bind(&ConnectionMap::value_type::second, _1)); - return result; -} - -// CPG config-change callback. -void Cluster::configChange ( - cpg_handle_t /*handle*/, - const cpg_name */*group*/, - const cpg_address *members, int nMembers, - const cpg_address *left, int nLeft, - const cpg_address *joined, int nJoined) -{ - Mutex::ScopedLock l(lock); - string membersStr, leftStr, joinedStr; - // Encode members and enqueue as an event so the config change can - // be executed in the correct thread. - for (const cpg_address* p = members; p < members+nMembers; ++p) - membersStr.append(MemberId(*p).str()); - for (const cpg_address* p = left; p < left+nLeft; ++p) - leftStr.append(MemberId(*p).str()); - for (const cpg_address* p = joined; p < joined+nJoined; ++p) - joinedStr.append(MemberId(*p).str()); - deliverEvent(Event::control(ClusterConfigChangeBody( - ProtocolVersion(), membersStr, leftStr, joinedStr), - self)); -} - -void Cluster::setReady(Lock&) { - state = READY; - mcast.setReady(); - broker.getQueueEvents().enable(); - enableClusterSafe(); // Enable cluster-safe assertions. -} - -// Set the management status from the Cluster::state. -// -// NOTE: Management updates are sent based on property changes. In -// order to keep consistency across the cluster, we touch the local -// management status property even if it is locally unchanged for any -// event that could have cause a cluster property change on any cluster member. -void Cluster::setMgmtStatus(Lock&) { - if (mgmtObject) - mgmtObject->set_status(state >= CATCHUP ? "ACTIVE" : "JOINING"); -} - -void Cluster::initMapCompleted(Lock& l) { - // Called on completion of the initial status map. - QPID_LOG(debug, *this << " initial status map complete. "); - setMgmtStatus(l); - if (state == PRE_INIT) { - // PRE_INIT means we're still in the earlyInitialize phase, in the constructor. - // We decide here whether we want to recover from our store. - // We won't recover if we are joining an active cluster or our store is dirty. - if (store.hasStore() && - store.getState() != STORE_STATE_EMPTY_STORE && - (initMap.isActive() || store.getState() == STORE_STATE_DIRTY_STORE)) - broker.setRecovery(false); // Ditch my current store. - state = INIT; - } - else if (state == INIT) { - // INIT means we are past Cluster::initialize(). - - // If we're forming an initial cluster (no active members) - // then we wait to reach the configured cluster-size - if (!initMap.isActive() && initMap.getActualSize() < initMap.getRequiredSize()) { - QPID_LOG(info, *this << initMap.getActualSize() - << " members, waiting for at least " << initMap.getRequiredSize()); - return; - } - - initMap.checkConsistent(); - elders = initMap.getElders(); - QPID_LOG(debug, *this << " elders: " << elders); - if (elders.empty()) - becomeElder(l); - else { - broker.getLinks().setPassive(true); - broker.getQueueEvents().disable(); - QPID_LOG(info, *this << " not active for links."); - } - setClusterId(initMap.getClusterId(), l); - - if (initMap.isUpdateNeeded()) { // Joining established cluster. - 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); - QPID_LOG(notice, *this << " joining cluster " << name); - } - else { // I can go ready. - discarding = false; - setReady(l); - // Must be called *before* memberUpdate so first update will be generated. - failoverExchange->setReady(); - memberUpdate(l); - updateMgmtMembership(l); - mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self); - QPID_LOG(notice, *this << " joined cluster " << name); - } - } -} - -void Cluster::configChange(const MemberId&, - const std::string& membersStr, - const std::string& leftStr, - const std::string& joinedStr, - Lock& l) -{ - if (state == LEFT) return; - MemberSet members = decodeMemberSet(membersStr); - MemberSet left = decodeMemberSet(leftStr); - MemberSet joined = decodeMemberSet(joinedStr); - QPID_LOG(notice, *this << " configuration change: " << members); - QPID_LOG_IF(notice, !left.empty(), *this << " Members left: " << left); - QPID_LOG_IF(notice, !joined.empty(), *this << " Members joined: " << joined); - - // If we are still joining, make sure there is someone to give us an update. - elders = intersection(elders, members); - if (elders.empty() && INIT < state && state < CATCHUP) { - QPID_LOG(critical, "Cannot update, all potential updaters left the cluster."); - leave(l); - return; - } - bool memberChange = map.configChange(members); - - // Update initital status for members joining or leaving. - initMap.configChange(members); - if (initMap.isResendNeeded()) { - mcast.mcastControl( - ClusterInitialStatusBody( - ProtocolVersion(), CLUSTER_VERSION, state > INIT, clusterId, - store.getState(), store.getShutdownId(), - initMap.getFirstConfigStr(), - vectorToUrlArray(getUrls(l)) - ), - self); - } - if (initMap.transitionToComplete()) initMapCompleted(l); - - if (state >= CATCHUP && memberChange) { - memberUpdate(l); - if (elders.empty()) becomeElder(l); - } - - updateMgmtMembership(l); // Update on every config change for consistency -} - -struct ClusterClockTask : public sys::TimerTask { - Cluster& cluster; - sys::Timer& timer; - - ClusterClockTask(Cluster& cluster, sys::Timer& timer, uint16_t clockInterval) - : TimerTask(Duration(clockInterval * TIME_MSEC),"ClusterClock"), cluster(cluster), timer(timer) {} - - void fire() { - cluster.sendClockUpdate(); - setupNextFire(); - timer.add(this); - } -}; - -void Cluster::becomeElder(Lock&) { - if (elder) return; // We were already the elder. - // We are the oldest, reactive links if necessary - QPID_LOG(info, *this << " became the elder, active for links."); - elder = true; - broker.getLinks().setPassive(false); - timer->becomeElder(); - - clockTimer.add(new ClusterClockTask(*this, clockTimer, settings.clockInterval)); -} - -void Cluster::makeOffer(const MemberId& id, Lock& ) { - if (state == READY && map.isJoiner(id)) { - state = OFFER; - QPID_LOG(info, *this << " send update-offer to " << id); - mcast.mcastControl(ClusterUpdateOfferBody(ProtocolVersion(), id), self); - } -} - -namespace { -struct AppendQueue { - ostream* os; - AppendQueue(ostream& o) : os(&o) {} - void operator()(const boost::shared_ptr<broker::Queue>& q) { - (*os) << " " << q->getName() << "=" << q->getMessageCount(); - } -}; -} // namespace - -// Log a snapshot of broker state, used for debugging inconsistency problems. -// May only be called in deliver thread. -std::string Cluster::debugSnapshot() { - assertClusterSafe(); - std::ostringstream msg; - msg << "Member joined, frameSeq=" << map.getFrameSeq() << ", queue snapshot:"; - AppendQueue append(msg); - broker.getQueues().eachQueue(append); - return msg.str(); -} - -// Called from Broker::~Broker when broker is shut down. At this -// point we know the poller has stopped so no poller callbacks will be -// invoked. We must ensure that CPG has also shut down so no CPG -// callbacks will be invoked. -// -void Cluster::brokerShutdown() { - sys::ClusterSafeScope css; // Don't trigger cluster-safe asserts. - try { cpg.shutdown(); } - catch (const std::exception& e) { - QPID_LOG(error, *this << " shutting down CPG: " << e.what()); - } - delete this; -} - -void Cluster::updateRequest(const MemberId& id, const std::string& url, Lock& l) { - map.updateRequest(id, url); - makeOffer(id, l); -} - -void Cluster::initialStatus(const MemberId& member, uint32_t version, bool active, - const framing::Uuid& id, - framing::cluster::StoreState store, - const framing::Uuid& shutdownId, - const std::string& firstConfig, - const framing::Array& urls, - Lock& l) -{ - if (version != CLUSTER_VERSION) { - QPID_LOG(critical, *this << " incompatible cluster versions " << - version << " != " << CLUSTER_VERSION); - leave(l); - return; - } - QPID_LOG_IF(debug, state == PRE_INIT, *this - << " received initial status from " << member); - initMap.received( - member, - ClusterInitialStatusBody(ProtocolVersion(), version, active, id, - store, shutdownId, firstConfig, urls) - ); - if (initMap.transitionToComplete()) initMapCompleted(l); -} - -void Cluster::ready(const MemberId& id, const std::string& url, Lock& l) { - try { - if (map.ready(id, Url(url))) - memberUpdate(l); - if (state == CATCHUP && id == self) { - setReady(l); - QPID_LOG(notice, *this << " caught up."); - } - } catch (const Url::Invalid& e) { - QPID_LOG(error, "Invalid URL in cluster ready command: " << url); - } - // Update management on every ready event to be consistent across cluster. - setMgmtStatus(l); - updateMgmtMembership(l); -} - -void Cluster::updateOffer(const MemberId& updater, uint64_t updateeInt, Lock& l) { - // NOTE: deliverEventQueue has been stopped at the update offer by - // deliveredEvent in case an update is required. - if (state == LEFT) return; - MemberId updatee(updateeInt); - boost::optional<Url> url = map.updateOffer(updater, updatee); - if (updater == self) { - assert(state == OFFER); - if (url) // My offer was first. - updateStart(updatee, *url, l); - else { // Another offer was first. - QPID_LOG(info, *this << " cancelled offer to " << updatee << " unstall"); - setReady(l); - makeOffer(map.firstJoiner(), l); // Maybe make another offer. - deliverEventQueue.start(); // Go back to normal processing - } - } - else if (updatee == self && url) { - assert(state == JOINER); - state = UPDATEE; - acl = broker.getAcl(); - broker.setAcl(0); // Disable ACL during update - QPID_LOG(notice, *this << " receiving update from " << updater); - checkUpdateIn(l); - } - else { - QPID_LOG(info, *this << " unstall, ignore update " << updater - << " to " << updatee); - deliverEventQueue.start(); // Not involved in update. - } - if (updatee != self && url) { - QPID_LOG(debug, debugSnapshot()); - if (mAgent) mAgent->clusterUpdate(); - // Updatee will call clusterUpdate() via checkUpdateIn() when update completes - } -} - -static client::ConnectionSettings connectionSettings(const ClusterSettings& settings) { - client::ConnectionSettings cs; - cs.username = settings.username; - cs.password = settings.password; - cs.mechanism = settings.mechanism; - return cs; -} - -void Cluster::retractOffer(const MemberId& updater, uint64_t updateeInt, Lock& l) { - // An offer was received while handling an error, and converted to a retract. - // Behavior is very similar to updateOffer. - if (state == LEFT) return; - MemberId updatee(updateeInt); - boost::optional<Url> url = map.updateOffer(updater, updatee); - if (updater == self) { - assert(state == OFFER); - if (url) { // My offer was first. - if (updateThread) - updateThread.join(); // Join the previous updateThread to avoid leaks. - updateThread = Thread(new RetractClient(*url, connectionSettings(settings))); - } - setReady(l); - makeOffer(map.firstJoiner(), l); // Maybe make another offer. - // Don't unstall the event queue, that was already done in deliveredFrame - } - QPID_LOG(debug,*this << " retracted offer " << updater << " to " << updatee); -} - -void Cluster::updateStart(const MemberId& updatee, const Url& url, Lock& l) { - // Check for credentials if authentication is enabled. - if (broker.getOptions().auth && !credentialsExchange->check(updatee)) { - QPID_LOG(error, "Un-authenticated attempt to join the cluster"); - return; - } - // NOTE: deliverEventQueue is already stopped at the stall point by deliveredEvent. - if (state == LEFT) return; - assert(state == OFFER); - state = UPDATER; - QPID_LOG(notice, *this << " sending update to " << updatee << " at " << url); - if (updateThread) - updateThread.join(); // Join the previous updateThread to avoid leaks. - updateThread = Thread( - new UpdateClient(self, updatee, url, broker, map, *expiryPolicy, - getConnections(l), decoder, - boost::bind(&Cluster::updateOutDone, this), - boost::bind(&Cluster::updateOutError, this, _1), - connectionSettings(settings))); -} - -// Called in network thread -void Cluster::updateInClosed() { - Lock l(lock); - assert(!updateClosed); - updateClosed = true; - checkUpdateIn(l); -} - -// Called in update thread. -void Cluster::updateInDone(const ClusterMap& m) { - Lock l(lock); - updatedMap = m; - checkUpdateIn(l); -} - -void Cluster::updateInRetracted() { - Lock l(lock); - updateRetracted = true; - map.clearStatus(); - checkUpdateIn(l); -} - -bool Cluster::isExpectingUpdate() { - Lock l(lock); - return state <= UPDATEE; -} - -// Called in update thread or deliver thread. -void Cluster::checkUpdateIn(Lock& l) { - if (state != UPDATEE) return; // Wait till we reach the stall point. - if (!updateClosed) return; // Wait till update connection closes. - if (updatedMap) { // We're up to date - map = *updatedMap; - mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self); - state = CATCHUP; - memberUpdate(l); - // Must be called *after* memberUpdate() to avoid sending an extra update. - failoverExchange->setReady(); - // NB: don't updateMgmtMembership() here as we are not in the deliver - // thread. It will be updated on delivery of the "ready" we just mcast. - broker.setClusterUpdatee(false); - broker.setAcl(acl); // Restore ACL - discarding = false; // OK to set, we're stalled for update. - QPID_LOG(notice, *this << " update complete, starting catch-up."); - QPID_LOG(debug, debugSnapshot()); // OK to call because we're stalled. - if (mAgent) { - // Update management agent now, after all update activity is complete. - updateDataExchange->updateManagementAgent(mAgent); - mAgent->suppress(false); // Enable management output. - mAgent->clusterUpdate(); - } - // Restore alternate exchange settings on exchanges. - broker.getExchanges().eachExchange( - boost::bind(&broker::Exchange::recoveryComplete, _1, - boost::ref(broker.getExchanges()))); - enableClusterSafe(); // Enable cluster-safe assertions - deliverEventQueue.start(); - // 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; - updateClosed = false; - state = JOINER; - QPID_LOG(notice, *this << " update retracted, sending new update request."); - mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self); - deliverEventQueue.start(); - } -} - -void Cluster::updateOutDone() { - Monitor::ScopedLock l(lock); - updateOutDone(l); -} - -void Cluster::updateOutDone(Lock& l) { - QPID_LOG(notice, *this << " update sent"); - assert(state == UPDATER); - state = READY; - deliverEventQueue.start(); // Start processing events again. - makeOffer(map.firstJoiner(), l); // Try another offer -} - -void Cluster::updateOutError(const std::exception& e) { - Monitor::ScopedLock l(lock); - QPID_LOG(error, *this << " error sending update: " << e.what()); - updateOutDone(l); -} - -void Cluster ::shutdown(const MemberId& , const Uuid& id, Lock& l) { - QPID_LOG(notice, *this << " cluster shut down by administrator."); - if (store.hasStore()) store.clean(id); - leave(l); -} - -ManagementObject* Cluster::GetManagementObject() const { return mgmtObject; } - -Manageable::status_t Cluster::ManagementMethod (uint32_t methodId, Args& args, string&) { - Lock l(lock); - QPID_LOG(debug, *this << " managementMethod [id=" << methodId << "]"); - switch (methodId) { - case _qmf::Cluster::METHOD_STOPCLUSTERNODE : - { - _qmf::ArgsClusterStopClusterNode& iargs = (_qmf::ArgsClusterStopClusterNode&) args; - stringstream stream; - stream << self; - if (iargs.i_brokerId == stream.str()) - stopClusterNode(l); - } - break; - case _qmf::Cluster::METHOD_STOPFULLCLUSTER : - stopFullCluster(l); - break; - default: - return Manageable::STATUS_UNKNOWN_METHOD; - } - return Manageable::STATUS_OK; -} - -void Cluster::stopClusterNode(Lock& l) { - QPID_LOG(notice, *this << " cluster member stopped by administrator."); - leave(l); -} - -void Cluster::stopFullCluster(Lock& ) { - QPID_LOG(notice, *this << " shutting down cluster " << name); - mcast.mcastControl(ClusterShutdownBody(ProtocolVersion(), Uuid(true)), self); -} - -void Cluster::memberUpdate(Lock& l) { - // Ignore config changes while we are joining. - if (state < CATCHUP) return; - QPID_LOG(info, *this << " member update: " << map); - size_t aliveCount = map.aliveCount(); - assert(map.isAlive(self)); - failoverExchange->updateUrls(getUrls(l)); - - // Mark store clean if I am the only broker, dirty otherwise. - if (store.hasStore()) { - if (aliveCount == 1) { - if (store.getState() != STORE_STATE_CLEAN_STORE) { - QPID_LOG(notice, *this << "Sole member of cluster, marking store clean."); - store.clean(Uuid(true)); - } - } - else { - if (store.getState() != STORE_STATE_DIRTY_STORE) { - QPID_LOG(notice, "Running in a cluster, marking store dirty."); - store.dirty(); - } - } - } - - // If I am the last member standing, set queue policies. - if (aliveCount == 1 && lastAliveCount > 1 && state >= CATCHUP) { - QPID_LOG(notice, *this << " last broker standing, update queue policies"); - lastBroker = true; - broker.getQueues().updateQueueClusterState(true); - } - else if (aliveCount > 1 && lastBroker) { - QPID_LOG(notice, *this << " last broker standing joined by " << aliveCount-1 - << " replicas, updating queue policies."); - lastBroker = false; - broker.getQueues().updateQueueClusterState(false); - } - lastAliveCount = aliveCount; - - // Close connections belonging to members that have left the cluster. - ConnectionMap::iterator i = connections.begin(); - while (i != connections.end()) { - ConnectionMap::iterator j = i++; - MemberId m = j->second->getId().getMember(); - if (m != self && !map.isMember(m)) { - j->second->close(); - erase(j->second->getId(), l); - } - } -} - -// See comment on Cluster::setMgmtStatus -void Cluster::updateMgmtMembership(Lock& l) { - if (!mgmtObject) return; - std::vector<Url> urls = getUrls(l); - mgmtObject->set_clusterSize(urls.size()); - string urlstr; - for(std::vector<Url>::iterator i = urls.begin(); i != urls.end(); i++ ) { - if (i != urls.begin()) urlstr += ";"; - urlstr += i->str(); - } - std::vector<string> ids = getIds(l); - string idstr; - for(std::vector<string>::iterator i = ids.begin(); i != ids.end(); i++ ) { - if (i != ids.begin()) idstr += ";"; - idstr += *i; - } - mgmtObject->set_members(urlstr); - mgmtObject->set_memberIDs(idstr); -} - -namespace { -template <class T> struct AutoClose { - T closeme; - AutoClose(T t) : closeme(t) {} - ~AutoClose() { closeme.close(); } -}; -} - -// Updatee connects to established member and stores credentials -// in the qpid.cluster-credentials exchange to prove it -// is safe for updater to connect and give an update. -void Cluster::authenticate() { - if (!broker.getOptions().auth) return; - std::vector<Url> urls = initMap.getUrls(); - for (std::vector<Url>::iterator i = urls.begin(); i != urls.end(); ++i) { - if (!i->empty()) { - client::Connection c; - c.open(*i, connectionSettings(settings)); - AutoClose<client::Connection> closeConnection(c); - client::Session s = c.newSession(CredentialsExchange::NAME); - AutoClose<client::Session> closeSession(s); - client::Message credentials; - credentials.getHeaders().setUInt64(CredentialsExchange::NAME, getId()); - s.messageTransfer(arg::content=credentials, arg::destination=CredentialsExchange::NAME); - s.sync(); - } - } -} - -std::ostream& operator<<(std::ostream& o, const Cluster& cluster) { - static const char* STATE[] = { - "PRE_INIT", "INIT", "JOINER", "UPDATEE", "CATCHUP", - "READY", "OFFER", "UPDATER", "LEFT" - }; - assert(sizeof(STATE)/sizeof(*STATE) == Cluster::LEFT+1); - o << "cluster(" << cluster.self << " " << STATE[cluster.state]; - if (cluster.error.isUnresolved()) o << "/error"; - return o << ")"; -} - -MemberId Cluster::getId() const { - return self; // Immutable, no need to lock. -} - -broker::Broker& Cluster::getBroker() const { - return broker; // Immutable, no need to lock. -} - -void Cluster::setClusterId(const Uuid& uuid, Lock&) { - clusterId = uuid; - if (store.hasStore()) store.setClusterId(uuid); - if (mgmtObject) { - stringstream stream; - stream << self; - mgmtObject->set_clusterID(clusterId.str()); - mgmtObject->set_memberID(stream.str()); - } - QPID_LOG(notice, *this << " cluster-uuid = " << clusterId); -} - -void Cluster::errorCheck(const MemberId& from, uint8_t type, framing::SequenceNumber frameSeq, Lock&) { - // If we see an errorCheck here (rather than in the ErrorCheck - // class) then we have processed succesfully past the point of the - // error. - if (state >= CATCHUP) // Don't respond pre catchup, we don't know what happened - error.respondNone(from, type, frameSeq); -} - -void Cluster::timerWakeup(const MemberId& , const std::string& name, Lock&) { - if (state >= CATCHUP) // Pre catchup our timer isn't set up. - timer->deliverWakeup(name); -} - -void Cluster::timerDrop(const MemberId& , const std::string& name, Lock&) { - QPID_LOG(debug, "Cluster timer drop " << map.getFrameSeq() << ": " << name) - if (state >= CATCHUP) // Pre catchup our timer isn't set up. - timer->deliverDrop(name); -} - -bool Cluster::isElder() const { - return elder; -} - -void Cluster::deliverToQueue(const std::string& queue, const std::string& message, Lock& l) -{ - broker::Queue::shared_ptr q = broker.getQueues().find(queue); - if (!q) { - QPID_LOG(critical, *this << " cluster delivery to non-existent queue: " << queue); - leave(l); - } - framing::Buffer buf(const_cast<char*>(message.data()), message.size()); - boost::intrusive_ptr<broker::Message> msg(new broker::Message); - msg->decodeHeader(buf); - msg->decodeContent(buf); - q->deliver(msg); -} - -sys::AbsTime Cluster::getClusterTime() { - Mutex::ScopedLock l(lock); - return clusterTime; -} - -// This method is called during update on the updatee to set the initial cluster time. -void Cluster::clock(const uint64_t time) { - Mutex::ScopedLock l(lock); - clock(time, l); -} - -// called when broadcast message received -void Cluster::clock(const uint64_t time, Lock&) { - clusterTime = AbsTime(EPOCH, time); - AbsTime now = AbsTime::now(); - - if (!elder) { - clusterTimeOffset = Duration(now, clusterTime); - } -} - -// called by elder timer to send clock broadcast -void Cluster::sendClockUpdate() { - Mutex::ScopedLock l(lock); - int64_t nanosecondsSinceEpoch = Duration(EPOCH, now()); - nanosecondsSinceEpoch += clusterTimeOffset; - mcast.mcastControl(ClusterClockBody(ProtocolVersion(), nanosecondsSinceEpoch), self); -} - -bool Cluster::deferDeliveryImpl(const std::string& queue, - const boost::intrusive_ptr<broker::Message>& msg) -{ - if (isClusterSafe()) return false; - std::string message; - message.resize(msg->encodedSize()); - framing::Buffer buf(const_cast<char*>(message.data()), message.size()); - msg->encode(buf); - mcast.mcastControl(ClusterDeliverToQueueBody(ProtocolVersion(), queue, message), self); - return true; -} - -bool Cluster::loggable(const AMQFrame& f) { - const AMQMethodBody* method = (f.getMethod()); - if (!method) return true; // Not a method - bool isClock = method->amqpClassId() == ClusterClockBody::CLASS_ID - && method->amqpMethodId() == ClusterClockBody::METHOD_ID; - return !isClock; -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/Cluster.h b/cpp/src/qpid/cluster/Cluster.h deleted file mode 100644 index 40f1445f23..0000000000 --- a/cpp/src/qpid/cluster/Cluster.h +++ /dev/null @@ -1,332 +0,0 @@ -#ifndef QPID_CLUSTER_CLUSTER_H -#define QPID_CLUSTER_CLUSTER_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 "ClusterMap.h" -#include "ClusterSettings.h" -#include "Cpg.h" -#include "Decoder.h" -#include "ErrorCheck.h" -#include "Event.h" -#include "EventFrame.h" -#include "ExpiryPolicy.h" -#include "FailoverExchange.h" -#include "InitialStatusMap.h" -#include "LockedConnectionMap.h" -#include "Multicaster.h" -#include "NoOpConnectionOutputHandler.h" -#include "PollableQueue.h" -#include "PollerDispatch.h" -#include "Quorum.h" -#include "StoreStatus.h" -#include "UpdateReceiver.h" - -#include "qmf/org/apache/qpid/cluster/Cluster.h" -#include "qpid/Url.h" -#include "qpid/broker/Broker.h" -#include "qpid/management/Manageable.h" -#include "qpid/sys/Monitor.h" - -#include <boost/bind.hpp> -#include <boost/intrusive_ptr.hpp> -#include <boost/optional.hpp> - -#include <algorithm> -#include <map> -#include <vector> - -namespace qpid { - -namespace broker { -class Message; -class AclModule; -} - -namespace framing { -class AMQFrame; -class AMQBody; -struct Uuid; -} - -namespace sys { -class Timer; -class AbsTime; -class Duration; -} - -namespace cluster { - -class Connection; -struct EventFrame; -class ClusterTimer; -class UpdateDataExchange; -class CredentialsExchange; - -/** - * Connection to the cluster - */ -class Cluster : private Cpg::Handler, public management::Manageable { - public: - typedef boost::intrusive_ptr<Connection> ConnectionPtr; - typedef std::vector<ConnectionPtr> ConnectionVector; - - // Public functions are thread safe unless otherwise mentioned in a comment. - - // Construct the cluster in plugin earlyInitialize. - Cluster(const ClusterSettings&, broker::Broker&); - virtual ~Cluster(); - - // Called by plugin initialize: cluster start-up requires transport plugins . - // Thread safety: only called by plugin initialize. - void initialize(); - - // Connection map. - void addLocalConnection(const ConnectionPtr&); - void addShadowConnection(const ConnectionPtr&); - void erase(const ConnectionId&); - void eraseLocal(const ConnectionId&); - - // URLs of current cluster members. - std::vector<std::string> getIds() const; - std::vector<Url> getUrls() const; - boost::shared_ptr<FailoverExchange> getFailoverExchange() const { return failoverExchange; } - - // Leave the cluster - called when fatal errors occur. - void leave(); - - // Update completed - called in update thread - void updateInClosed(); - void updateInDone(const ClusterMap&); - void updateInRetracted(); - // True if we are expecting to receive catch-up connections. - bool isExpectingUpdate(); - - MemberId getId() const; - broker::Broker& getBroker() const; - Multicaster& getMulticast() { return mcast; } - - const ClusterSettings& getSettings() const { return settings; } - - void deliverFrame(const EventFrame&); - - // Called in deliverFrame thread to indicate an error from the broker. - void flagError(Connection&, ErrorCheck::ErrorType, const std::string& msg); - - // Called only during update by Connection::shadowReady - Decoder& getDecoder() { return decoder; } - - ExpiryPolicy& getExpiryPolicy() { return *expiryPolicy; } - - UpdateReceiver& getUpdateReceiver() { return updateReceiver; } - - bool isElder() const; - - // Generates a log message for debugging purposes. - std::string debugSnapshot(); - - // Defer messages delivered in an unsafe context by multicasting. - bool deferDeliveryImpl(const std::string& queue, - const boost::intrusive_ptr<broker::Message>& msg); - - sys::AbsTime getClusterTime(); - void sendClockUpdate(); - void clock(const uint64_t time); - - static bool loggable(const framing::AMQFrame&); // True if the frame should be logged. - - private: - typedef sys::Monitor::ScopedLock Lock; - - typedef PollableQueue<Event> PollableEventQueue; - typedef PollableQueue<EventFrame> PollableFrameQueue; - typedef std::map<ConnectionId, ConnectionPtr> ConnectionMap; - - /** Version number of the cluster protocol, to avoid mixed versions. */ - static const uint32_t CLUSTER_VERSION; - - // NB: A dummy Lock& parameter marks functions that must only be - // called with Cluster::lock locked. - - void leave(Lock&); - std::vector<std::string> getIds(Lock&) const; - std::vector<Url> getUrls(Lock&) const; - - // == Called in main thread from Broker destructor. - void brokerShutdown(); - - // == Called in deliverEventQueue thread - void deliveredEvent(const Event&); - - // == Called in deliverFrameQueue thread - void deliveredFrame(const EventFrame&); - void processFrame(const EventFrame&, Lock&); - - // Cluster controls implement XML methods from cluster.xml. - void updateRequest(const MemberId&, const std::string&, Lock&); - void updateOffer(const MemberId& updater, uint64_t updatee, Lock&); - void retractOffer(const MemberId& updater, uint64_t updatee, Lock&); - void initialStatus(const MemberId&, - uint32_t version, - bool active, - const framing::Uuid& clusterId, - framing::cluster::StoreState, - const framing::Uuid& shutdownId, - const std::string& firstConfig, - const framing::Array& urls, - Lock&); - void ready(const MemberId&, const std::string&, Lock&); - void configChange(const MemberId&, - const std::string& members, - const std::string& left, - const std::string& joined, - Lock& l); - void errorCheck(const MemberId&, uint8_t type, SequenceNumber frameSeq, Lock&); - void timerWakeup(const MemberId&, const std::string& name, Lock&); - void timerDrop(const MemberId&, const std::string& name, Lock&); - void shutdown(const MemberId&, const framing::Uuid& shutdownId, Lock&); - void deliverToQueue(const std::string& queue, const std::string& message, Lock&); - void clock(const uint64_t time, Lock&); - - // Helper functions - ConnectionPtr getConnection(const EventFrame&, Lock&); - ConnectionVector getConnections(Lock&); - void updateStart(const MemberId& updatee, const Url& url, Lock&); - void makeOffer(const MemberId&, Lock&); - void setReady(Lock&); - void memberUpdate(Lock&); - void setClusterId(const framing::Uuid&, Lock&); - void erase(const ConnectionId&, Lock&); - void eraseLocal(const ConnectionId&, Lock&); - void requestUpdate(Lock& ); - void initMapCompleted(Lock&); - void becomeElder(Lock&); - void setMgmtStatus(Lock&); - void updateMgmtMembership(Lock&); - void authenticate(); - - // == Called in CPG dispatch thread - void deliver( // CPG deliver callback. - cpg_handle_t /*handle*/, - const struct cpg_name *group, - uint32_t /*nodeid*/, - uint32_t /*pid*/, - void* /*msg*/, - int /*msg_len*/); - - void deliverEvent(const Event&); - - void configChange( // CPG config change callback. - cpg_handle_t /*handle*/, - const struct cpg_name */*group*/, - const struct cpg_address */*members*/, int /*nMembers*/, - const struct cpg_address */*left*/, int /*nLeft*/, - const struct cpg_address */*joined*/, int /*nJoined*/ - ); - - // == Called in management threads. - virtual qpid::management::ManagementObject* GetManagementObject() const; - virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); - - void stopClusterNode(Lock&); - void stopFullCluster(Lock&); - - // == Called in connection IO threads . - void checkUpdateIn(Lock&); - - // == Called in UpdateClient thread. - void updateOutDone(); - void updateOutError(const std::exception&); - void updateOutDone(Lock&); - - // Immutable members set on construction, never changed. - const ClusterSettings settings; - broker::Broker& broker; - qmf::org::apache::qpid::cluster::Cluster* mgmtObject; // mgnt owns lifecycle - boost::shared_ptr<sys::Poller> poller; - Cpg cpg; - const std::string name; - Url myUrl; - const MemberId self; - framing::Uuid clusterId; - NoOpConnectionOutputHandler shadowOut; - qpid::management::ManagementAgent* mAgent; - boost::intrusive_ptr<ExpiryPolicy> expiryPolicy; - - // Thread safe members - Multicaster mcast; - PollerDispatch dispatcher; - PollableEventQueue deliverEventQueue; - PollableFrameQueue deliverFrameQueue; - boost::shared_ptr<FailoverExchange> failoverExchange; - boost::shared_ptr<UpdateDataExchange> updateDataExchange; - boost::shared_ptr<CredentialsExchange> credentialsExchange; - Quorum quorum; - LockedConnectionMap localConnections; - - // Used only in deliverEventQueue thread or when stalled for update. - Decoder decoder; - bool discarding; - - - // Remaining members are protected by lock. - mutable sys::Monitor lock; - - - // Local cluster state, cluster map - enum { - PRE_INIT,///< Have not yet received complete initial status map. - INIT, ///< Waiting to reach cluster-size. - JOINER, ///< Sent update request, waiting for update offer. - UPDATEE, ///< Stalled receive queue at update offer, waiting for update to complete. - CATCHUP, ///< Update complete, unstalled but has not yet seen own "ready" event. - READY, ///< Fully operational - OFFER, ///< Sent an offer, waiting for accept/reject. - UPDATER, ///< Offer accepted, sending a state update. - LEFT ///< Final state, left the cluster. - } state; - - ConnectionMap connections; - InitialStatusMap initMap; - StoreStatus store; - ClusterMap map; - MemberSet elders; - bool elder; - size_t lastAliveCount; - bool lastBroker; - sys::Thread updateThread; - boost::optional<ClusterMap> updatedMap; - bool updateRetracted, updateClosed; - ErrorCheck error; - UpdateReceiver updateReceiver; - ClusterTimer* timer; - sys::Timer clockTimer; - sys::AbsTime clusterTime; - sys::Duration clusterTimeOffset; - broker::AclModule* acl; - - friend std::ostream& operator<<(std::ostream&, const Cluster&); - friend struct ClusterDispatcher; -}; - -}} // namespace qpid::cluster - - - -#endif /*!QPID_CLUSTER_CLUSTER_H*/ diff --git a/cpp/src/qpid/cluster/ClusterMap.cpp b/cpp/src/qpid/cluster/ClusterMap.cpp deleted file mode 100644 index d9817db35f..0000000000 --- a/cpp/src/qpid/cluster/ClusterMap.cpp +++ /dev/null @@ -1,178 +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/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> -#include <functional> -#include <iterator> -#include <ostream> - -using namespace std; -using boost::ref; -using boost::optional; - -namespace qpid { -using namespace framing; - -namespace cluster { - -namespace { - -void addFieldTableValue(FieldTable::ValueMap::value_type vt, ClusterMap::Map& map, ClusterMap::Set& set) { - MemberId id(vt.first); - set.insert(id); - string url = vt.second->get<string>(); - if (!url.empty()) - map.insert(ClusterMap::Map::value_type(id, Url(url))); -} - -void insertFieldTableFromMapValue(FieldTable& ft, const ClusterMap::Map::value_type& vt) { - ft.setString(vt.first.str(), vt.second.str()); -} - -} - -ClusterMap::ClusterMap() : frameSeq(0) {} - -ClusterMap::ClusterMap(const Map& map) : frameSeq(0) { - transform(map.begin(), map.end(), inserter(alive, alive.begin()), bind(&Map::value_type::first, _1)); - members = map; -} - -ClusterMap::ClusterMap(const FieldTable& joinersFt, const FieldTable& membersFt, - framing::SequenceNumber frameSeq_) - : frameSeq(frameSeq_) -{ - for_each(joinersFt.begin(), joinersFt.end(), bind(&addFieldTableValue, _1, ref(joiners), ref(alive))); - for_each(membersFt.begin(), membersFt.end(), bind(&addFieldTableValue, _1, ref(members), ref(alive))); -} - -void ClusterMap::toMethodBody(framing::ClusterConnectionMembershipBody& b) const { - b.getJoiners().clear(); - for_each(joiners.begin(), joiners.end(), bind(&insertFieldTableFromMapValue, ref(b.getJoiners()), _1)); - for(Set::const_iterator i = alive.begin(); i != alive.end(); ++i) { - if (!isMember(*i) && !isJoiner(*i)) - b.getJoiners().setString(i->str(), string()); - } - b.getMembers().clear(); - for_each(members.begin(), members.end(), bind(&insertFieldTableFromMapValue, ref(b.getMembers()), _1)); - b.setFrameSeq(frameSeq); -} - -Url ClusterMap::getUrl(const Map& map, const MemberId& id) { - Map::const_iterator i = map.find(id); - return i == map.end() ? Url() : i->second; -} - -MemberId ClusterMap::firstJoiner() const { - return joiners.empty() ? MemberId() : joiners.begin()->first; -} - -vector<string> ClusterMap::memberIds() const { - vector<string> ids; - for (Map::const_iterator iter = members.begin(); - iter != members.end(); iter++) { - stringstream stream; - stream << iter->first; - ids.push_back(stream.str()); - } - return ids; -} - -vector<Url> ClusterMap::memberUrls() const { - vector<Url> urls(members.size()); - transform(members.begin(), members.end(), urls.begin(), - bind(&Map::value_type::second, _1)); - return urls; -} - -ClusterMap::Set ClusterMap::getAlive() const { return alive; } - -ClusterMap::Set ClusterMap::getMembers() const { - Set s; - transform(members.begin(), members.end(), inserter(s, s.begin()), - bind(&Map::value_type::first, _1)); - return s; -} - -ostream& operator<<(ostream& o, const ClusterMap::Map& m) { - ostream_iterator<MemberId> oi(o); - transform(m.begin(), m.end(), oi, bind(&ClusterMap::Map::value_type::first, _1)); - return o; -} - -ostream& operator<<(ostream& o, const ClusterMap& m) { - for (ClusterMap::Set::const_iterator i = m.alive.begin(); i != m.alive.end(); ++i) { - o << *i; - if (m.isMember(*i)) o << "(member)"; - else if (m.isJoiner(*i)) o << "(joiner)"; - else o << "(unknown)"; - o << " "; - } - o << "frameSeq=" << m.getFrameSeq(); - return o; -} - -bool ClusterMap::updateRequest(const MemberId& id, const string& url) { - try { - if (isAlive(id)) { - joiners[id] = Url(url); - return true; - } - } catch (const Url::Invalid&) { - QPID_LOG(error, "Invalid URL in cluster update request: " << url); - } - return false; -} - -bool ClusterMap::ready(const MemberId& id, const Url& url) { - return isAlive(id) && members.insert(Map::value_type(id,url)).second; -} - -bool ClusterMap::configChange(const Set& update) { - bool memberChange = false; - Set removed; - set_difference(alive.begin(), alive.end(), - update.begin(), update.end(), - inserter(removed, removed.begin())); - alive = update; - for (Set::const_iterator i = removed.begin(); i != removed.end(); ++i) { - memberChange = memberChange || members.erase(*i); - joiners.erase(*i); - } - return memberChange; -} - -optional<Url> ClusterMap::updateOffer(const MemberId& from, const MemberId& to) { - Map::iterator i = joiners.find(to); - if (isAlive(from) && i != joiners.end()) { - Url url= i->second; - joiners.erase(i); // No longer a potential updatee. - return url; - } - return optional<Url>(); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/ClusterMap.h b/cpp/src/qpid/cluster/ClusterMap.h deleted file mode 100644 index cfa4ad924a..0000000000 --- a/cpp/src/qpid/cluster/ClusterMap.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef QPID_CLUSTER_CLUSTERMAP_H -#define QPID_CLUSTER_CLUSTERMAP_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 "MemberSet.h" -#include "qpid/Url.h" -#include "qpid/framing/ClusterConnectionMembershipBody.h" -#include "qpid/framing/SequenceNumber.h" - -#include <boost/function.hpp> -#include <boost/optional.hpp> - -#include <vector> -#include <deque> -#include <map> -#include <iosfwd> - -namespace qpid { -namespace cluster { - -/** - * Map of established cluster members and joiners waiting for an update, - * along with other cluster state that must be updated. - */ -class ClusterMap { - public: - typedef std::map<MemberId, Url> Map; - typedef std::set<MemberId> Set; - - ClusterMap(); - ClusterMap(const Map& map); - ClusterMap(const framing::FieldTable& joiners, const framing::FieldTable& members, - framing::SequenceNumber frameSeq); - - /** Update from config change. - *@return true if member set changed. - */ - bool configChange(const Set& members); - - bool isJoiner(const MemberId& id) const { return joiners.find(id) != joiners.end(); } - bool isMember(const MemberId& id) const { return members.find(id) != members.end(); } - bool isAlive(const MemberId& id) const { return alive.find(id) != alive.end(); } - - Url getJoinerUrl(const MemberId& id) { return getUrl(joiners, id); } - Url getMemberUrl(const MemberId& id) { return getUrl(members, id); } - - /** First joiner in the cluster in ID order, target for offers */ - MemberId firstJoiner() const; - - /** Convert map contents to a cluster control body. */ - void toMethodBody(framing::ClusterConnectionMembershipBody&) const; - - size_t aliveCount() const { return alive.size(); } - size_t memberCount() const { return members.size(); } - std::vector<std::string> memberIds() const; - std::vector<Url> memberUrls() const; - Set getAlive() const; - Set getMembers() const; - - bool updateRequest(const MemberId& id, const std::string& url); - /** Return non-empty Url if accepted */ - boost::optional<Url> updateOffer(const MemberId& from, const MemberId& to); - - /**@return true If this is a new member */ - bool ready(const MemberId& id, const Url&); - - framing::SequenceNumber getFrameSeq() const { return frameSeq; } - framing::SequenceNumber incrementFrameSeq() { return ++frameSeq; } - - /** Clear out all knowledge of joiners & members, just keep alive set */ - void clearStatus() { joiners.clear(); members.clear(); } - - private: - Url getUrl(const Map& map, const MemberId& id); - - Map joiners, members; - Set alive; - framing::SequenceNumber frameSeq; - - friend std::ostream& operator<<(std::ostream&, const Map&); - friend std::ostream& operator<<(std::ostream&, const ClusterMap&); -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_CLUSTERMAP_H*/ diff --git a/cpp/src/qpid/cluster/ClusterPlugin.cpp b/cpp/src/qpid/cluster/ClusterPlugin.cpp deleted file mode 100644 index 69ba095f16..0000000000 --- a/cpp/src/qpid/cluster/ClusterPlugin.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "config.h" -#include "qpid/cluster/Connection.h" -#include "qpid/cluster/ConnectionCodec.h" -#include "qpid/cluster/ClusterSettings.h" - -#include "qpid/cluster/SecureConnectionFactory.h" - -#include "qpid/cluster/Cluster.h" -#include "qpid/cluster/ConnectionCodec.h" -#include "qpid/cluster/UpdateClient.h" - -#include "qpid/broker/Broker.h" -#include "qpid/Plugin.h" -#include "qpid/Options.h" -#include "qpid/sys/AtomicValue.h" -#include "qpid/log/Statement.h" - -#include "qpid/management/ManagementAgent.h" -#include "qpid/broker/Exchange.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/SessionState.h" -#include "qpid/client/ConnectionSettings.h" - -#include <boost/shared_ptr.hpp> -#include <boost/utility/in_place_factory.hpp> -#include <boost/scoped_ptr.hpp> - -namespace qpid { -namespace cluster { - -using namespace std; -using broker::Broker; -using management::ManagementAgent; - - -/** Note separating options from settings to work around boost version differences. - * Old boost takes a reference to options objects, but new boost makes a copy. - * New boost allows a shared_ptr but that's not compatible with old boost. - */ -struct ClusterOptions : public Options { - ClusterSettings& settings; - - ClusterOptions(ClusterSettings& v) : Options("Cluster Options"), settings(v) { - addOptions() - ("cluster-name", optValue(settings.name, "NAME"), "Name of cluster to join") - ("cluster-url", optValue(settings.url,"URL"), - "Set URL of this individual broker, to be advertized to clients.\n" - "Defaults to a URL listing all the local IP addresses\n") - ("cluster-username", optValue(settings.username, ""), "Username for connections between brokers") - ("cluster-password", optValue(settings.password, ""), "Password for connections between brokers") - ("cluster-mechanism", optValue(settings.mechanism, ""), "Authentication mechanism for connections between brokers") -#if HAVE_LIBCMAN_H - ("cluster-cman", optValue(settings.quorum), "Integrate with Cluster Manager (CMAN) cluster.") -#endif - ("cluster-size", optValue(settings.size, "N"), "Wait for N cluster members before allowing clients to connect.") - ("cluster-clock-interval", optValue(settings.clockInterval,"N"), "How often to broadcast the current time to the cluster nodes, in milliseconds. A value between 5 and 1000 is recommended.") - ("cluster-read-max", optValue(settings.readMax,"N"), "Experimental: flow-control limit reads per connection. 0=no limit.") - ; - } -}; - -typedef boost::shared_ptr<sys::ConnectionCodec::Factory> CodecFactoryPtr; - -struct ClusterPlugin : public Plugin { - - ClusterSettings settings; - ClusterOptions options; - Cluster* cluster; - boost::scoped_ptr<ConnectionCodec::Factory> factory; - - ClusterPlugin() : options(settings), cluster(0) {} - - // Cluster needs to be initialized after the store - int initOrder() const { return Plugin::DEFAULT_INIT_ORDER+500; } - - Options* getOptions() { return &options; } - - void earlyInitialize(Plugin::Target& target) { - if (settings.name.empty()) return; // Only if --cluster-name option was specified. - Broker* broker = dynamic_cast<Broker*>(&target); - if (!broker) return; - cluster = new Cluster(settings, *broker); - CodecFactoryPtr simpleFactory(new broker::ConnectionFactory(*broker)); - CodecFactoryPtr clusterFactory(new ConnectionCodec::Factory(simpleFactory, *cluster)); - CodecFactoryPtr secureFactory(new SecureConnectionFactory(clusterFactory)); - broker->setConnectionFactory(secureFactory); - } - - void disallowManagementMethods(ManagementAgent* agent) { - if (!agent) return; - agent->disallowV1Methods(); - } - - void initialize(Plugin::Target& target) { - Broker* broker = dynamic_cast<Broker*>(&target); - if (broker && cluster) { - disallowManagementMethods(broker->getManagementAgent()); - cluster->initialize(); - } - } -}; - -static ClusterPlugin instance; // Static initialization. - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/ClusterSettings.h b/cpp/src/qpid/cluster/ClusterSettings.h deleted file mode 100644 index 2f7b5be20a..0000000000 --- a/cpp/src/qpid/cluster/ClusterSettings.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef QPID_CLUSTER_CLUSTERSETTINGS_H -#define QPID_CLUSTER_CLUSTERSETTINGS_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/Url.h> -#include <string> - -namespace qpid { -namespace cluster { - -struct ClusterSettings { - std::string name; - std::string url; - bool quorum; - size_t readMax; - std::string username, password, mechanism; - size_t size; - uint16_t clockInterval; - - ClusterSettings() : quorum(false), readMax(10), size(1), clockInterval(10) - {} - - Url getUrl(uint16_t port) const { - if (url.empty()) return Url::getIpAddressesUrl(port); - return Url(url); - } -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_CLUSTERSETTINGS_H*/ diff --git a/cpp/src/qpid/cluster/ClusterTimer.cpp b/cpp/src/qpid/cluster/ClusterTimer.cpp deleted file mode 100644 index 90e4fa9d4d..0000000000 --- a/cpp/src/qpid/cluster/ClusterTimer.cpp +++ /dev/null @@ -1,144 +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 "Cluster.h" -#include "ClusterTimer.h" -#include "qpid/log/Statement.h" -#include "qpid/framing/ClusterTimerWakeupBody.h" -#include "qpid/framing/ClusterTimerDropBody.h" -#include "qpid/sys/ClusterSafe.h" - -namespace qpid { -namespace cluster { - -using boost::intrusive_ptr; -using std::max; -using sys::Timer; -using sys::TimerTask; - -// -// Note on use of Broker::getTimer() rather than getClusterTime in broker code. -// The following uses of getTimer() are cluster safe: -// -// LinkRegistry: maintenance visits in timer can call Bridge::create/cancel -// but these don't modify any management state. -// -// broker::Connection: -// - Heartbeats use ClusterOrderOutput to ensure consistency -// - timeout: aborts connection in timer, cluster does an orderly connection close. -// -// SessionState: scheduledCredit - uses ClusterOrderProxy -// Broker::queueCleaner: cluster implements ExpiryPolicy for consistent expiry. -// -// Broker::dtxManager: dtx disabled with cluster. -// -// requestIOProcessing: called in doOutput. -// - - -ClusterTimer::ClusterTimer(Cluster& c) : cluster(c) { - // Allow more generous overrun threshold with cluster as we - // have to do a CPG round trip before executing the task. - overran = 10*sys::TIME_MSEC; - late = 100*sys::TIME_MSEC; -} - -ClusterTimer::~ClusterTimer() {} - -// Initialization or deliver thread. -void ClusterTimer::add(intrusive_ptr<TimerTask> task) -{ - QPID_LOG(trace, "Adding cluster timer task " << task->getName()); - Map::iterator i = map.find(task->getName()); - if (i != map.end()) - throw Exception(QPID_MSG("Task already exists with name " << task->getName())); - map[task->getName()] = task; - - // Only the elder actually activates the task with the Timer base class. - if (cluster.isElder()) { - QPID_LOG(trace, "Elder activating cluster timer task " << task->getName()); - Timer::add(task); - } -} - -// Timer thread -void ClusterTimer::fire(intrusive_ptr<TimerTask> t) { - // Elder mcasts wakeup on fire, task is not fired until deliverWakeup - if (cluster.isElder()) { - QPID_LOG(trace, "Sending cluster timer wakeup " << t->getName()); - cluster.getMulticast().mcastControl( - framing::ClusterTimerWakeupBody(framing::ProtocolVersion(), t->getName()), - cluster.getId()); - } - else - QPID_LOG(trace, "Cluster timer task fired, but not elder " << t->getName()); -} - -// Timer thread -void ClusterTimer::drop(intrusive_ptr<TimerTask> t) { - // Elder mcasts drop, task is droped in deliverDrop - if (cluster.isElder()) { - QPID_LOG(trace, "Sending cluster timer drop " << t->getName()); - cluster.getMulticast().mcastControl( - framing::ClusterTimerDropBody(framing::ProtocolVersion(), t->getName()), - cluster.getId()); - } - else - QPID_LOG(trace, "Cluster timer task dropped, but not on elder " << t->getName()); -} - -// 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)); - else { - intrusive_ptr<TimerTask> t = i->second; - map.erase(i); - // Move the nextFireTime so readyToFire() is true. This is to ensure we - // don't get an error if the fired task calls setupNextFire() - t->setFired(); - Timer::fire(t); - } -} - -// Deliver thread -void ClusterTimer::deliverDrop(const std::string& name) { - QPID_LOG(trace, "Cluster timer drop delivered for " << name); - Map::iterator i = map.find(name); - if (i == map.end()) - throw Exception(QPID_MSG("Cluster timer drop non-existent task " << name)); - else { - intrusive_ptr<TimerTask> t = i->second; - map.erase(i); - } -} - -// Deliver thread -void ClusterTimer::becomeElder() { - for (Map::iterator i = map.begin(); i != map.end(); ++i) { - Timer::add(i->second); - } -} - -}} diff --git a/cpp/src/qpid/cluster/ClusterTimer.h b/cpp/src/qpid/cluster/ClusterTimer.h deleted file mode 100644 index 69f6c622e4..0000000000 --- a/cpp/src/qpid/cluster/ClusterTimer.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef QPID_CLUSTER_CLUSTERTIMER_H -#define QPID_CLUSTER_CLUSTERTIMER_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/Timer.h" -#include <map> - -namespace qpid { -namespace cluster { - -class Cluster; - -/** - * Timer implementation that executes tasks consistently in the - * deliver thread across a cluster. Task is not executed when timer - * fires, instead the elder multicasts a wakeup. The task is executed - * when the wakeup is delivered. - */ -class ClusterTimer : public sys::Timer { - public: - ClusterTimer(Cluster&); - ~ClusterTimer(); - - void add(boost::intrusive_ptr<sys::TimerTask> task); - - void deliverWakeup(const std::string& name); - void deliverDrop(const std::string& name); - void becomeElder(); - - protected: - void fire(boost::intrusive_ptr<sys::TimerTask> task); - void drop(boost::intrusive_ptr<sys::TimerTask> task); - - private: - typedef std::map<std::string, boost::intrusive_ptr<sys::TimerTask> > Map; - Cluster& cluster; - Map map; -}; - - -}} - - -#endif /*!QPID_CLUSTER_CLUSTERTIMER_H*/ diff --git a/cpp/src/qpid/cluster/Connection.cpp b/cpp/src/qpid/cluster/Connection.cpp deleted file mode 100644 index ff855eef18..0000000000 --- a/cpp/src/qpid/cluster/Connection.cpp +++ /dev/null @@ -1,878 +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/amqp_0_10/Codecs.h" -#include "Connection.h" -#include "UpdateClient.h" -#include "Cluster.h" -#include "UpdateReceiver.h" -#include "qpid/assert.h" -#include "qpid/broker/DtxAck.h" -#include "qpid/broker/DtxBuffer.h" -#include "qpid/broker/SessionState.h" -#include "qpid/broker/SemanticState.h" -#include "qpid/broker/TxBuffer.h" -#include "qpid/broker/TxPublish.h" -#include "qpid/broker/TxAccept.h" -#include "qpid/broker/RecoveredEnqueue.h" -#include "qpid/broker/RecoveredDequeue.h" -#include "qpid/broker/Exchange.h" -#include "qpid/broker/Fairshare.h" -#include "qpid/broker/Link.h" -#include "qpid/broker/Bridge.h" -#include "qpid/broker/StatefulQueueObserver.h" -#include "qpid/broker/Queue.h" -#include "qpid/framing/enum.h" -#include "qpid/framing/AMQFrame.h" -#include "qpid/framing/AllInvoker.h" -#include "qpid/framing/DeliveryProperties.h" -#include "qpid/framing/ClusterConnectionDeliverCloseBody.h" -#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" -#include "qpid/management/ManagementAgent.h" -#include <boost/current_function.hpp> - - -namespace qpid { -namespace cluster { - -using std::string; - -using namespace framing; -using namespace framing::cluster; -using amqp_0_10::ListCodec; -using types::Variant; - -qpid::sys::AtomicValue<uint64_t> Connection::catchUpId(0x5000000000000000LL); - -Connection::NullFrameHandler Connection::nullFrameHandler; - -struct NullFrameHandler : public framing::FrameHandler { - void handle(framing::AMQFrame&) {} -}; - - -namespace { -sys::AtomicValue<uint64_t> idCounter; -const std::string shadowPrefix("[shadow]"); -} - - -// Shadow connection -Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, - const std::string& mgmtId, - const ConnectionId& id, const qpid::sys::SecuritySettings& external) - : cluster(c), self(id), catchUp(false), announced(false), output(*this, out), - connectionCtor(&output, cluster.getBroker(), mgmtId, external, - false/*isLink*/, 0/*objectId*/, true/*shadow*/, false/*delayManagement*/, - false/*authenticated*/), - expectProtocolHeader(false), - mcastFrameHandler(cluster.getMulticast(), self), - updateIn(c.getUpdateReceiver()), - secureConnection(0) -{} - -// Local connection -Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out, - const std::string& mgmtId, MemberId member, - bool isCatchUp, bool isLink, const qpid::sys::SecuritySettings& external -) : cluster(c), self(member, ++idCounter), catchUp(isCatchUp), announced(false), output(*this, out), - connectionCtor(&output, cluster.getBroker(), - mgmtId, - external, - isLink, - isCatchUp ? ++catchUpId : 0, - // The first catch-up connection is not a shadow - isCatchUp && self.second > 1, - false, // delayManagement - true), // catch up connecytions are authenticated - expectProtocolHeader(isLink), - mcastFrameHandler(cluster.getMulticast(), self), - updateIn(c.getUpdateReceiver()), - secureConnection(0) -{ - if (isLocalClient()) { - giveReadCredit(cluster.getSettings().readMax); // Flow control - // Delay adding the connection to the management map until announce() - connectionCtor.delayManagement = true; - } - else { - // Catch-up shadow connections initialized using nextShadow id. - assert(catchUp); - if (!updateIn.nextShadowMgmtId.empty()) - connectionCtor.mgmtId = updateIn.nextShadowMgmtId; - updateIn.nextShadowMgmtId.clear(); - } - init(); - QPID_LOG(debug, cluster << " local connection " << *this); -} - -void Connection::setSecureConnection(broker::SecureConnection* sc) { - secureConnection = sc; - if (connection.get()) connection->setSecureConnection(sc); -} - -void Connection::init() { - connection = connectionCtor.construct(); - if (isLocalClient()) { - if (secureConnection) connection->setSecureConnection(secureConnection); - // Actively send cluster-order frames from local node - connection->setClusterOrderOutput(mcastFrameHandler); - } - else { // Shadow or catch-up connection - // Passive, discard cluster-order frames - connection->setClusterOrderOutput(nullFrameHandler); - } - if (!isCatchUp()) - connection->setErrorListener(this); -} - -// Called when we have consumed a read buffer to give credit to the -// connection layer to continue reading. -void Connection::giveReadCredit(int credit) { - if (cluster.getSettings().readMax && credit) - output.giveReadCredit(credit); -} - -void Connection::announce( - const std::string& mgmtId, uint32_t ssf, const std::string& authid, bool nodict, - const std::string& username, const std::string& initialFrames) -{ - QPID_ASSERT(mgmtId == connectionCtor.mgmtId); - QPID_ASSERT(ssf == connectionCtor.external.ssf); - QPID_ASSERT(authid == connectionCtor.external.authid); - QPID_ASSERT(nodict == connectionCtor.external.nodict); - // Local connections are already initialized but with management delayed. - if (isLocalClient()) { - connection->addManagementObject(); - } - else if (isShadow()) { - init(); - // Play initial frames into the connection. - Buffer buf(const_cast<char*>(initialFrames.data()), initialFrames.size()); - AMQFrame frame; - while (frame.decode(buf)) - connection->received(frame); - connection->setUserId(username); - } - // Do managment actions now that the connection is replicated. - connection->raiseConnectEvent(); - QPID_LOG(debug, cluster << " replicated connection " << *this); -} - -Connection::~Connection() { - if (connection.get()) connection->setErrorListener(0); - // Don't trigger cluster-safe asserts in broker:: ~Connection as - // it may be called in an IO thread context during broker - // shutdown. - sys::ClusterSafeScope css; - connection.reset(); -} - -bool Connection::doOutput() { - return output.doOutput(); -} - -// Received from a directly connected client. -void Connection::received(framing::AMQFrame& f) { - if (!connection.get()) { - QPID_LOG(warning, cluster << " ignoring frame on closed connection " - << *this << ": " << f); - return; - } - QPID_LOG_IF(trace, Cluster::loggable(f), cluster << " RECV " << *this << ": " << f); - if (isLocal()) { // Local catch-up connection. - currentChannel = f.getChannel(); - if (!framing::invoke(*this, *f.getBody()).wasHandled()) - connection->received(f); - } - else { // Shadow or updated catch-up connection. - if (f.getMethod() && f.getMethod()->isA<ConnectionCloseBody>()) { - if (isShadow()) - cluster.addShadowConnection(this); - AMQFrame ok((ConnectionCloseOkBody())); - connection->getOutput().send(ok); - output.closeOutput(); - catchUp = false; - } - else - QPID_LOG(warning, cluster << " ignoring unexpected frame " << *this << ": " << f); - } -} - -bool Connection::checkUnsupported(const AMQBody&) { - // Throw an exception for unsupported commands. Currently all are supported. - return false; -} - -struct GiveReadCreditOnExit { - Connection& connection; - int credit; - GiveReadCreditOnExit(Connection& connection_, int credit_) : - connection(connection_), credit(credit_) {} - ~GiveReadCreditOnExit() { if (credit) connection.giveReadCredit(credit); } -}; - -void Connection::deliverDoOutput(uint32_t limit) { - output.deliverDoOutput(limit); -} - -// Called in delivery thread, in cluster order. -void Connection::deliveredFrame(const EventFrame& f) { - GiveReadCreditOnExit gc(*this, f.readCredit); - assert(!catchUp); - currentChannel = f.frame.getChannel(); - if (f.frame.getBody() // frame can be emtpy with just readCredit - && !framing::invoke(*this, *f.frame.getBody()).wasHandled() // Connection contol. - && !checkUnsupported(*f.frame.getBody())) // Unsupported operation. - { - if (f.type == DATA) // incoming data frames to broker::Connection - connection->received(const_cast<AMQFrame&>(f.frame)); - else { // frame control, send frame via SessionState - broker::SessionState* ss = connection->getChannel(currentChannel).getSession(); - if (ss) ss->out(const_cast<AMQFrame&>(f.frame)); - } - } -} - -// A local connection is closed by the network layer. Called in the connection thread. -void Connection::closed() { - try { - if (isUpdated()) { - QPID_LOG(debug, cluster << " update connection closed " << *this); - close(); - cluster.updateInClosed(); - } - else if (catchUp && cluster.isExpectingUpdate()) { - QPID_LOG(critical, cluster << " catch-up connection closed prematurely " << *this); - cluster.leave(); - } - else if (isLocal()) { - // This was a local replicated connection. Multicast a deliver - // closed and process any outstanding frames from the cluster - // until self-delivery of deliver-close. - output.closeOutput(); - if (announced) - cluster.getMulticast().mcastControl( - ClusterConnectionDeliverCloseBody(), self); - else - close(); - } - } - catch (const std::exception& e) { - QPID_LOG(error, cluster << " error closing connection " << *this << ": " << e.what()); - } -} - -// Self-delivery of close message, close the connection. -void Connection::deliverClose () { - close(); - cluster.erase(self); -} - -// Close the connection -void Connection::close() { - if (connection.get()) { - QPID_LOG(debug, cluster << " closed connection " << *this); - connection->closed(); - connection.reset(); - } -} - -// The connection has sent invalid data and should be aborted. -// All members will get the same abort since they all process the same data. -void Connection::abort() { - connection->abort(); - // Aborting the connection will result in a call to ::closed() - // and allow the connection to close in an orderly manner. -} - -// ConnectionCodec::decode receives read buffers from directly-connected clients. -size_t Connection::decode(const char* data, size_t size) { - GiveReadCreditOnExit grc(*this, 1); // Give a read credit by default. - const char* ptr = data; - const char* end = data + size; - if (catchUp) { // Handle catch-up locally. - if (!cluster.isExpectingUpdate()) { - QPID_LOG(error, "Rejecting unexpected catch-up connection."); - abort(); // Cluster is not expecting catch-up connections. - } - bool wasOpen = connection->isOpen(); - Buffer buf(const_cast<char*>(ptr), size); - ptr += size; - while (localDecoder.decode(buf)) - received(localDecoder.getFrame()); - if (!wasOpen && connection->isOpen()) { - // Connections marked with setUserProxyAuth are allowed to proxy - // messages with user-ID that doesn't match the connection's - // authenticated ID. This is important for updates. - connection->setUserProxyAuth(isCatchUp()); - } - } - else { // Multicast local connections. - assert(isLocalClient()); - assert(connection.get()); - if (!checkProtocolHeader(ptr, size)) // Updates ptr - return 0; // Incomplete header - - if (!connection->isOpen()) - processInitialFrames(ptr, end-ptr); // Updates ptr - - if (connection->isOpen() && end - ptr > 0) { - // We're multi-casting, we will give read credit on delivery. - grc.credit = 0; - cluster.getMulticast().mcastBuffer(ptr, end - ptr, self); - ptr = end; - } - } - return ptr - data; -} - -// Decode the protocol header if needed. Updates data and size -// returns true if the header is complete or already read. -bool Connection::checkProtocolHeader(const char*& data, size_t size) { - if (expectProtocolHeader) { - // This is an outgoing link connection, we will receive a protocol - // header which needs to be decoded first - framing::ProtocolInitiation pi; - Buffer buf(const_cast<char*&>(data), size); - if (pi.decode(buf)) { - //TODO: check the version is correct - expectProtocolHeader = false; - data += pi.encodedSize(); - } else { - return false; - } - } - return true; -} - -void Connection::processInitialFrames(const char*& ptr, size_t size) { - // Process the initial negotiation locally and store it so - // it can be replayed on other brokers in announce() - Buffer buf(const_cast<char*>(ptr), size); - framing::AMQFrame frame; - while (!connection->isOpen() && frame.decode(buf)) - received(frame); - initialFrames.append(ptr, buf.getPosition()); - ptr += buf.getPosition(); - if (connection->isOpen()) { // initial negotiation complete - cluster.getMulticast().mcastControl( - ClusterConnectionAnnounceBody( - ProtocolVersion(), - connectionCtor.mgmtId, - connectionCtor.external.ssf, - connectionCtor.external.authid, - connectionCtor.external.nodict, - connection->getUserId(), - initialFrames), - getId()); - announced = true; - initialFrames.clear(); - } -} - -broker::SessionState& Connection::sessionState() { - return *connection->getChannel(currentChannel).getSession(); -} - -broker::SemanticState& Connection::semanticState() { - return sessionState().getSemanticState(); -} - -void Connection::shadowPrepare(const std::string& mgmtId) { - updateIn.nextShadowMgmtId = mgmtId; -} - -void Connection::shadowSetUser(const std::string& userId) { - connection->setUserId(userId); -} - -void Connection::consumerState(const string& name, bool blocked, bool notifyEnabled, const SequenceNumber& position, - uint32_t usedMsgCredit, uint32_t usedByteCredit, const uint32_t deliveryCount) -{ - broker::SemanticState::ConsumerImpl::shared_ptr c = semanticState().find(name); - c->setPosition(position); - c->setBlocked(blocked); - c->setDeliveryCount(deliveryCount); - if (c->getCredit().isWindowMode()) c->getCredit().consume(usedMsgCredit, usedByteCredit); - if (notifyEnabled) c->enableNotify(); else c->disableNotify(); - updateIn.consumerNumbering.add(c); -} - - -void Connection::sessionState( - const SequenceNumber& replayStart, - const SequenceNumber& sendCommandPoint, - const SequenceSet& sentIncomplete, - const SequenceNumber& expected, - const SequenceNumber& received, - const SequenceSet& unknownCompleted, - const SequenceSet& receivedIncomplete, - bool dtxSelected) -{ - sessionState().setState( - replayStart, - sendCommandPoint, - sentIncomplete, - expected, - received, - unknownCompleted, - receivedIncomplete); - if (dtxSelected) semanticState().selectDtx(); - QPID_LOG(debug, cluster << " received session state update for " - << sessionState().getId()); - // The output tasks will be added later in the update process. - connection->getOutputTasks().removeAll(); -} - -void Connection::outputTask(uint16_t channel, const std::string& name) { - broker::SessionState* session = connection->getChannel(channel).getSession(); - if (!session) - throw Exception(QPID_MSG(cluster << " channel not attached " << *this - << "[" << channel << "] ")); - OutputTask* task = session->getSemanticState().find(name).get(); - connection->getOutputTasks().addOutputTask(task); -} - -void Connection::shadowReady( - uint64_t memberId, uint64_t connectionId, const string& mgmtId, - const string& username, const string& fragment, uint32_t sendMax) -{ - QPID_ASSERT(mgmtId == getBrokerConnection()->getMgmtId()); - ConnectionId shadowId = ConnectionId(memberId, connectionId); - QPID_LOG(debug, cluster << " catch-up connection " << *this - << " becomes shadow " << shadowId); - self = shadowId; - connection->setUserId(username); - // OK to use decoder here because cluster is stalled for update. - cluster.getDecoder().get(self).setFragment(fragment.data(), fragment.size()); - connection->setErrorListener(this); - output.setSendMax(sendMax); -} - -void Connection::setDtxBuffer(const UpdateReceiver::DtxBufferRef& bufRef) { - broker::DtxManager& mgr = cluster.getBroker().getDtxManager(); - broker::DtxWorkRecord* record = mgr.getWork(bufRef.xid); - broker::DtxBuffer::shared_ptr buffer = (*record)[bufRef.index]; - if (bufRef.suspended) - bufRef.semanticState->getSuspendedXids()[bufRef.xid] = buffer; - else - bufRef.semanticState->setDtxBuffer(buffer); -} - -// Marks the end of the update. -void Connection::membership(const FieldTable& joiners, const FieldTable& members, - const framing::SequenceNumber& frameSeq) -{ - QPID_LOG(debug, cluster << " incoming update complete on connection " << *this); - updateIn.consumerNumbering.clear(); - for_each(updateIn.dtxBuffers.begin(), updateIn.dtxBuffers.end(), - boost::bind(&Connection::setDtxBuffer, this, _1)); - closeUpdated(); - cluster.updateInDone(ClusterMap(joiners, members, frameSeq)); -} - -void Connection::retractOffer() { - QPID_LOG(info, cluster << " incoming update retracted on connection " << *this); - closeUpdated(); - cluster.updateInRetracted(); -} - -void Connection::closeUpdated() { - self.second = 0; // Mark this as completed update connection. - if (connection.get()) - connection->close(connection::CLOSE_CODE_NORMAL, "OK"); -} - -bool Connection::isLocal() const { - return self.first == cluster.getId() && self.second; -} - -bool Connection::isShadow() const { - return self.first != cluster.getId(); -} - -bool Connection::isUpdated() const { - return self.first == cluster.getId() && self.second == 0; -} - - -boost::shared_ptr<broker::Queue> Connection::findQueue(const std::string& qname) { - boost::shared_ptr<broker::Queue> queue = cluster.getBroker().getQueues().find(qname); - if (!queue) throw Exception(QPID_MSG(cluster << " can't find queue " << qname)); - return queue; -} - -broker::QueuedMessage Connection::getUpdateMessage() { - boost::shared_ptr<broker::Queue> updateq = findQueue(UpdateClient::UPDATE); - assert(!updateq->isDurable()); - broker::QueuedMessage m = updateq->get(); - updateq->dequeue(0, m); - if (!m.payload) throw Exception(QPID_MSG(cluster << " empty update queue")); - return m; -} - -void Connection::deliveryRecord(const string& qname, - const SequenceNumber& position, - const string& tag, - const SequenceNumber& id, - bool acquired, - bool accepted, - bool cancelled, - bool completed, - bool ended, - bool windowing, - bool enqueued, - uint32_t credit) -{ - broker::QueuedMessage m; - broker::Queue::shared_ptr queue = findQueue(qname); - if (!ended) { // Has a message - if (acquired) { // Message is on the update queue - m = getUpdateMessage(); - m.queue = queue.get(); - m.position = position; - if (enqueued) queue->updateEnqueued(m); //inform queue of the message - } else { // Message at original position in original queue - queue->find(position, m); - } - // NOTE: removed: - // if (!m.payload) - // throw Exception(QPID_MSG("deliveryRecord no update message")); - // - // It seems this could happen legitimately in the case one - // session browses message M, then another session acquires - // it. In that case the browsers delivery record is !acquired - // but the message is not on its original Queue. In that case - // we'll get a deliveryRecord with no payload for the browser. - // - } - - // If a subscription is cancelled while there are unacked messages for it - // there won't be a consumer. Just null it out in this case, it isn't needed. - boost::shared_ptr<broker::Consumer> consumer; - try { consumer = semanticState().find(tag); } - catch(...) {} - - broker::DeliveryRecord dr( - m, queue, tag, consumer, acquired, accepted, windowing, credit); - dr.setId(id); - if (cancelled) dr.cancel(dr.getTag()); - if (completed) dr.complete(); - if (ended) dr.setEnded(); // Exsitance of message - - if (dtxBuffer) // Record for next dtx-ack - dtxAckRecords.push_back(dr); - else - semanticState().record(dr); // Record on session's unacked list. -} - -void Connection::queuePosition(const string& qname, const SequenceNumber& position) { - findQueue(qname)->setPosition(position); -} - -void Connection::queueFairshareState(const std::string& qname, const uint8_t priority, const uint8_t count) -{ - if (!qpid::broker::Fairshare::setState(findQueue(qname)->getMessages(), priority, count)) { - QPID_LOG(error, "Failed to set fair share state on queue " << qname << "; this will result in inconsistencies."); - } -} - - -namespace { -// find a StatefulQueueObserver that matches a given identifier -class ObserverFinder { - const std::string id; - boost::shared_ptr<broker::QueueObserver> target; - ObserverFinder(const ObserverFinder&) {} - public: - ObserverFinder(const std::string& _id) : id(_id) {} - broker::StatefulQueueObserver *getObserver() - { - if (target) - return dynamic_cast<broker::StatefulQueueObserver *>(target.get()); - return 0; - } - void operator() (boost::shared_ptr<broker::QueueObserver> o) - { - if (!target) { - broker::StatefulQueueObserver *p = dynamic_cast<broker::StatefulQueueObserver *>(o.get()); - if (p && p->getId() == id) { - target = o; - } - } - } -}; -} - - -void Connection::queueObserverState(const std::string& qname, const std::string& observerId, const FieldTable& state) -{ - boost::shared_ptr<broker::Queue> queue(findQueue(qname)); - ObserverFinder finder(observerId); // find this observer - queue->eachObserver<ObserverFinder &>(finder); - broker::StatefulQueueObserver *so = finder.getObserver(); - if (so) { - so->setState( state ); - QPID_LOG(debug, "updated queue observer " << observerId << "'s state on queue " << qname << "; ..."); - return; - } - QPID_LOG(error, "Failed to find observer " << observerId << " state on queue " << qname << "; this will result in inconsistencies."); -} - -std::ostream& operator<<(std::ostream& o, const Connection& c) { - const char* type="unknown"; - if (c.isLocal()) type = "local"; - else if (c.isShadow()) type = "shadow"; - else if (c.isUpdated()) type = "updated"; - const broker::Connection* bc = c.getBrokerConnection(); - if (bc) o << bc->getMgmtId(); - else o << "<disconnected>"; - return o << "(" << c.getId() << " " << type << (c.isCatchUp() ? ",catchup":"") << ")"; -} - -void Connection::txStart() { - txBuffer.reset(new broker::TxBuffer()); -} - -void Connection::txAccept(const framing::SequenceSet& acked) { - txBuffer->enlist(boost::shared_ptr<broker::TxAccept>( - new broker::TxAccept(acked, semanticState().getUnacked()))); -} - -void Connection::txDequeue(const std::string& queue) { - txBuffer->enlist(boost::shared_ptr<broker::RecoveredDequeue>( - new broker::RecoveredDequeue(findQueue(queue), getUpdateMessage().payload))); -} - -void Connection::txEnqueue(const std::string& queue) { - txBuffer->enlist(boost::shared_ptr<broker::RecoveredEnqueue>( - new broker::RecoveredEnqueue(findQueue(queue), getUpdateMessage().payload))); -} - -void Connection::txPublish(const framing::Array& queues, bool delivered) -{ - boost::shared_ptr<broker::TxPublish> txPub( - new broker::TxPublish(getUpdateMessage().payload)); - for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i) - txPub->deliverTo(findQueue((*i)->get<std::string>())); - txPub->delivered = delivered; - txBuffer->enlist(txPub); -} - -void Connection::txEnd() { - semanticState().setTxBuffer(txBuffer); -} - -void Connection::accumulatedAck(const qpid::framing::SequenceSet& s) { - semanticState().setAccumulatedAck(s); -} - -void Connection::dtxStart(const std::string& xid, - bool ended, - bool suspended, - bool failed, - bool expired) -{ - dtxBuffer.reset(new broker::DtxBuffer(xid, ended, suspended, failed, expired)); - txBuffer = dtxBuffer; -} - -void Connection::dtxEnd() { - broker::DtxManager& mgr = cluster.getBroker().getDtxManager(); - std::string xid = dtxBuffer->getXid(); - if (mgr.exists(xid)) - mgr.join(xid, dtxBuffer); - else - mgr.start(xid, dtxBuffer); - dtxBuffer.reset(); - txBuffer.reset(); -} - -// Sent after all DeliveryRecords for a dtx-ack have been collected in dtxAckRecords -void Connection::dtxAck() { - dtxBuffer->enlist( - boost::shared_ptr<broker::DtxAck>(new broker::DtxAck(dtxAckRecords))); - dtxAckRecords.clear(); -} - -void Connection::dtxBufferRef(const std::string& xid, uint32_t index, bool suspended) { - // Save the association between DtxBuffers and the session so we - // can set the DtxBuffers at the end of the update when the - // DtxManager has been replicated. - updateIn.dtxBuffers.push_back( - UpdateReceiver::DtxBufferRef(xid, index, suspended, &semanticState())); -} - -// Sent at end of work record. -void Connection::dtxWorkRecord(const std::string& xid, bool prepared, uint32_t timeout) -{ - broker::DtxManager& mgr = cluster.getBroker().getDtxManager(); - if (timeout) mgr.setTimeout(xid, timeout); - if (prepared) mgr.prepare(xid); -} - - -void Connection::exchange(const std::string& encoded) { - Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); - broker::Exchange::shared_ptr ex = broker::Exchange::decode(cluster.getBroker().getExchanges(), buf); - if(ex.get() && ex->isDurable() && !ex->getName().find("amq.") == 0 && !ex->getName().find("qpid.") == 0) { - cluster.getBroker().getStore().create(*(ex.get()), ex->getArgs()); - } - QPID_LOG(debug, cluster << " updated exchange " << ex->getName()); -} - -void Connection::sessionError(uint16_t , const std::string& msg) { - // Ignore errors before isOpen(), we're not multicasting yet. - if (connection->isOpen()) - cluster.flagError(*this, ERROR_TYPE_SESSION, msg); -} - -void Connection::connectionError(const std::string& msg) { - // Ignore errors before isOpen(), we're not multicasting yet. - if (connection->isOpen()) { - cluster.flagError(*this, ERROR_TYPE_CONNECTION, msg); - } - else - cluster.eraseLocal(self); -} - -void Connection::addQueueListener(const std::string& q, uint32_t listener) { - if (listener >= updateIn.consumerNumbering.size()) - throw Exception(QPID_MSG("Invalid listener ID: " << listener)); - findQueue(q)->getListeners().addListener(updateIn.consumerNumbering[listener]); -} - -// -// This is the handler for incoming managementsetup messages. -// -void Connection::managementSetupState( - uint64_t objectNum, uint16_t bootSequence, const framing::Uuid& id, - const std::string& vendor, const std::string& product, const std::string& instance) -{ - QPID_LOG(debug, cluster << " updated management: object number=" - << objectNum << " boot sequence=" << bootSequence - << " broker-id=" << id - << " vendor=" << vendor - << " product=" << product - << " instance=" << instance); - management::ManagementAgent* agent = cluster.getBroker().getManagementAgent(); - if (!agent) - throw Exception(QPID_MSG("Management schema update but management not enabled.")); - agent->setNextObjectId(objectNum); - agent->setBootSequence(bootSequence); - agent->setUuid(id); - agent->setName(vendor, product, instance); -} - -void Connection::config(const std::string& encoded) { - Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); - string kind; - uint32_t p = buf.getPosition(); - buf.getShortString (kind); - buf.setPosition(p); - if (broker::Link::isEncodedLink(kind)) { - broker::Link::shared_ptr link = - broker::Link::decode(cluster.getBroker().getLinks(), buf); - QPID_LOG(debug, cluster << " updated link " - << link->getHost() << ":" << link->getPort()); - } - else if (broker::Bridge::isEncodedBridge(kind)) { - broker::Bridge::shared_ptr bridge = - broker::Bridge::decode(cluster.getBroker().getLinks(), buf); - QPID_LOG(debug, cluster << " updated bridge " << bridge->getName()); - } - 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 - // transferred during catch-up. - - if (catchUp) getBrokerConnection()->doIoCallbacks(); -} - -void Connection::clock(uint64_t time) { - QPID_LOG(debug, "Cluster connection received time update"); - cluster.clock(time); -} - -void Connection::queueDequeueSincePurgeState(const std::string& qname, uint32_t dequeueSincePurge) { - boost::shared_ptr<broker::Queue> queue(findQueue(qname)); - queue->setDequeueSincePurge(dequeueSincePurge); -} - -}} // Namespace qpid::cluster - diff --git a/cpp/src/qpid/cluster/Connection.h b/cpp/src/qpid/cluster/Connection.h deleted file mode 100644 index b0e7b3bd9e..0000000000 --- a/cpp/src/qpid/cluster/Connection.h +++ /dev/null @@ -1,302 +0,0 @@ -#ifndef QPID_CLUSTER_CONNECTION_H -#define QPID_CLUSTER_CONNECTION_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 "types.h" -#include "OutputInterceptor.h" -#include "McastFrameHandler.h" -#include "UpdateReceiver.h" - -#include "qpid/RefCounted.h" -#include "qpid/broker/Connection.h" -#include "qpid/broker/DeliveryRecord.h" -#include "qpid/broker/SecureConnection.h" -#include "qpid/broker/SemanticState.h" -#include "qpid/amqp_0_10/Connection.h" -#include "qpid/sys/AtomicValue.h" -#include "qpid/sys/ConnectionInputHandler.h" -#include "qpid/sys/ConnectionOutputHandler.h" -#include "qpid/sys/SecuritySettings.h" -#include "qpid/framing/SequenceNumber.h" -#include "qpid/framing/FrameDecoder.h" - -#include <iosfwd> - -namespace qpid { - -namespace framing { class AMQFrame; } - -namespace broker { -class SemanticState; -struct QueuedMessage; -class TxBuffer; -class TxAccept; -} - -namespace cluster { -class Cluster; -class Event; -struct EventFrame; - -/** Intercept broker::Connection calls for shadow and local cluster connections. */ -class Connection : - public RefCounted, - public sys::ConnectionInputHandler, - public framing::AMQP_AllOperations::ClusterConnectionHandler, - private broker::Connection::ErrorListener - -{ - public: - - /** Local connection. */ - Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, MemberId, bool catchUp, bool isLink, - const qpid::sys::SecuritySettings& external); - /** Shadow connection. */ - Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, const ConnectionId& id, - const qpid::sys::SecuritySettings& external); - ~Connection(); - - ConnectionId getId() const { return self; } - broker::Connection* getBrokerConnection() { return connection.get(); } - const broker::Connection* getBrokerConnection() const { return connection.get(); } - - /** Local connections may be clients or catch-up connections */ - bool isLocal() const; - - bool isLocalClient() const { return isLocal() && !isCatchUp(); } - - /** True for connections that are shadowing remote broker connections */ - bool isShadow() const; - - /** True if the connection is in "catch-up" mode: building initial broker state. */ - bool isCatchUp() const { return catchUp; } - - /** True if the connection is a completed shared update connection */ - bool isUpdated() const; - - Cluster& getCluster() { return cluster; } - - // ConnectionInputHandler methods - void received(framing::AMQFrame&); - void closed(); - bool doOutput(); - void idleOut() { if (connection.get()) connection->idleOut(); } - void idleIn() { if (connection.get()) connection->idleIn(); } - - // ConnectionCodec methods - called by IO layer with a read buffer. - size_t decode(const char* buffer, size_t size); - - // Called for data delivered from the cluster. - void deliveredFrame(const EventFrame&); - - void consumerState(const std::string& name, bool blocked, bool notifyEnabled, const qpid::framing::SequenceNumber& position, - uint32_t usedMsgCredit, uint32_t usedByteCredit, const uint32_t deliveryCount); - - // ==== Used in catch-up mode to build initial state. - // - // State update methods. - void shadowPrepare(const std::string&); - - void shadowSetUser(const std::string&); - - void sessionState(const framing::SequenceNumber& replayStart, - const framing::SequenceNumber& sendCommandPoint, - const framing::SequenceSet& sentIncomplete, - const framing::SequenceNumber& expected, - const framing::SequenceNumber& received, - const framing::SequenceSet& unknownCompleted, - const SequenceSet& receivedIncomplete, - bool dtxSelected); - - void outputTask(uint16_t channel, const std::string& name); - - void shadowReady(uint64_t memberId, - uint64_t connectionId, - const std::string& managementId, - const std::string& username, - const std::string& fragment, - uint32_t sendMax); - - void membership(const framing::FieldTable&, const framing::FieldTable&, - const framing::SequenceNumber& frameSeq); - - void retractOffer(); - - void deliveryRecord(const std::string& queue, - const framing::SequenceNumber& position, - const std::string& tag, - const framing::SequenceNumber& id, - bool acquired, - bool accepted, - bool cancelled, - bool completed, - bool ended, - bool windowing, - bool enqueued, - uint32_t credit); - - void queuePosition(const std::string&, const framing::SequenceNumber&); - void queueFairshareState(const std::string&, const uint8_t priority, const uint8_t count); - void queueObserverState(const std::string&, const std::string&, const framing::FieldTable&); - - void txStart(); - void txAccept(const framing::SequenceSet&); - void txDequeue(const std::string&); - void txEnqueue(const std::string&); - void txPublish(const framing::Array&, bool); - void txEnd(); - void accumulatedAck(const framing::SequenceSet&); - - // Dtx state - void dtxStart(const std::string& xid, - bool ended, - bool suspended, - bool failed, - bool expired); - void dtxEnd(); - void dtxAck(); - void dtxBufferRef(const std::string& xid, uint32_t index, bool suspended); - void dtxWorkRecord(const std::string& xid, bool prepared, uint32_t timeout); - - // Encoded exchange replication. - void exchange(const std::string& encoded); - - void giveReadCredit(int credit); - void announce(const std::string& mgmtId, uint32_t ssf, const std::string& authid, - bool nodict, const std::string& username, - const std::string& initFrames); - void close(); - void abort(); - void deliverClose(); - - OutputInterceptor& getOutput() { return output; } - - void addQueueListener(const std::string& queue, uint32_t listener); - void managementSetupState(uint64_t objectNum, - uint16_t bootSequence, - const framing::Uuid&, - const std::string& vendor, - const std::string& product, - 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 ); - - void doCatchupIoCallbacks(); - - void clock(uint64_t time); - - void queueDequeueSincePurgeState(const std::string&, uint32_t); - - bool isAnnounced() const { return announced; } - - private: - struct NullFrameHandler : public framing::FrameHandler { - void handle(framing::AMQFrame&) {} - }; - - // Arguments to construct a broker::Connection - struct ConnectionCtor { - sys::ConnectionOutputHandler* out; - broker::Broker& broker; - std::string mgmtId; - qpid::sys::SecuritySettings external; - bool isLink; - uint64_t objectId; - bool shadow; - bool delayManagement; - bool authenticated; - - ConnectionCtor( - sys::ConnectionOutputHandler* out_, - broker::Broker& broker_, - const std::string& mgmtId_, - const qpid::sys::SecuritySettings& external_, - bool isLink_=false, - uint64_t objectId_=0, - bool shadow_=false, - bool delayManagement_=false, - bool authenticated_=true - ) : out(out_), broker(broker_), mgmtId(mgmtId_), external(external_), - isLink(isLink_), objectId(objectId_), shadow(shadow_), - delayManagement(delayManagement_), authenticated(authenticated_) - {} - - std::auto_ptr<broker::Connection> construct() { - return std::auto_ptr<broker::Connection>( - new broker::Connection( - out, broker, mgmtId, external, isLink, objectId, - shadow, delayManagement, authenticated) - ); - } - }; - - static NullFrameHandler nullFrameHandler; - - // Error listener functions - void connectionError(const std::string&); - void sessionError(uint16_t channel, const std::string&); - - void init(); - bool checkUnsupported(const framing::AMQBody& body); - void deliverDoOutput(uint32_t limit); - - bool checkProtocolHeader(const char*& data, size_t size); - void processInitialFrames(const char*& data, size_t size); - boost::shared_ptr<broker::Queue> findQueue(const std::string& qname); - broker::SessionState& sessionState(); - broker::SemanticState& semanticState(); - broker::QueuedMessage getUpdateMessage(); - void closeUpdated(); - void setDtxBuffer(const UpdateReceiver::DtxBuffers::value_type &); - Cluster& cluster; - ConnectionId self; - bool catchUp; - bool announced; - OutputInterceptor output; - framing::FrameDecoder localDecoder; - ConnectionCtor connectionCtor; - std::auto_ptr<broker::Connection> connection; - framing::SequenceNumber deliverSeq; - framing::ChannelId currentChannel; - boost::shared_ptr<broker::TxBuffer> txBuffer; - boost::shared_ptr<broker::DtxBuffer> dtxBuffer; - broker::DeliveryRecords dtxAckRecords; - broker::DtxWorkRecord* dtxCurrent; - bool expectProtocolHeader; - McastFrameHandler mcastFrameHandler; - UpdateReceiver& updateIn; - qpid::broker::SecureConnection* secureConnection; - std::string initialFrames; - - static qpid::sys::AtomicValue<uint64_t> catchUpId; - - friend std::ostream& operator<<(std::ostream&, const Connection&); -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_CONNECTION_H*/ diff --git a/cpp/src/qpid/cluster/ConnectionCodec.cpp b/cpp/src/qpid/cluster/ConnectionCodec.cpp deleted file mode 100644 index 54327fbfe2..0000000000 --- a/cpp/src/qpid/cluster/ConnectionCodec.cpp +++ /dev/null @@ -1,86 +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/cluster/ConnectionCodec.h" -#include "qpid/cluster/Connection.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/cluster/ProxyInputHandler.h" -#include "qpid/broker/AclModule.h" -#include "qpid/broker/Connection.h" -#include "qpid/framing/ConnectionCloseBody.h" -#include "qpid/framing/ConnectionCloseOkBody.h" -#include "qpid/log/Statement.h" -#include "qpid/memory.h" -#include <stdexcept> -#include <boost/utility/in_place_factory.hpp> - -namespace qpid { -namespace cluster { - -using namespace framing; - -sys::ConnectionCodec* -ConnectionCodec::Factory::create(ProtocolVersion v, sys::OutputControl& out, - const std::string& id, - const qpid::sys::SecuritySettings& external) -{ - if (v == ProtocolVersion(0, 10)) - return new ConnectionCodec(v, out, id, cluster, false, false, external); - else if (v == ProtocolVersion(0x80 + 0, 0x80 + 10)) // Catch-up connection - return new ConnectionCodec(v, out, id, cluster, true, false, external); - return 0; -} - -// Used for outgoing Link connections -sys::ConnectionCodec* -ConnectionCodec::Factory::create(sys::OutputControl& out, const std::string& logId, - const qpid::sys::SecuritySettings& external) { - return new ConnectionCodec(ProtocolVersion(0,10), out, logId, cluster, false, true, external); -} - -ConnectionCodec::ConnectionCodec( - const ProtocolVersion& v, sys::OutputControl& out, - const std::string& logId, Cluster& cluster, bool catchUp, bool isLink, const qpid::sys::SecuritySettings& external -) : codec(out, logId, isLink), - interceptor(new Connection(cluster, codec, logId, cluster.getId(), catchUp, isLink, external)) -{ - cluster.addLocalConnection(interceptor); - std::auto_ptr<sys::ConnectionInputHandler> ih(new ProxyInputHandler(interceptor)); - codec.setInputHandler(ih); - codec.setVersion(v); -} - -ConnectionCodec::~ConnectionCodec() {} - -size_t ConnectionCodec::decode(const char* buffer, size_t size) { - return interceptor->decode(buffer, size); -} - -bool ConnectionCodec::isClosed() const { return codec.isClosed(); } - -size_t ConnectionCodec::encode(const char* buffer, size_t size) { return codec.encode(buffer, size); } - -bool ConnectionCodec::canEncode() { return codec.canEncode(); } - -void ConnectionCodec::closed() { codec.closed(); } - -ProtocolVersion ConnectionCodec::getVersion() const { return codec.getVersion(); } - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/ConnectionCodec.h b/cpp/src/qpid/cluster/ConnectionCodec.h deleted file mode 100644 index 17a08904d9..0000000000 --- a/cpp/src/qpid/cluster/ConnectionCodec.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef QPID_CLUSTER_CONNCTIONCODEC_H -#define QPID_CLUSTER_CONNCTIONCODEC_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/amqp_0_10/Connection.h" -#include "qpid/cluster/Connection.h" -#include <boost/shared_ptr.hpp> -#include <boost/intrusive_ptr.hpp> - -namespace qpid { - -namespace broker { -class Connection; -} - -namespace cluster { -class Cluster; - -/** - * Encapsulates the standard amqp_0_10::ConnectionCodec and sets up - * a cluster::Connection for the connection. - * - * The ConnectionCodec is deleted by the network layer when the - * connection closes. The cluster::Connection needs to be kept - * around until all cluster business on the connection is complete. - * - */ -class ConnectionCodec : public sys::ConnectionCodec { - public: - struct Factory : public sys::ConnectionCodec::Factory { - boost::shared_ptr<sys::ConnectionCodec::Factory> next; - Cluster& cluster; - Factory(boost::shared_ptr<sys::ConnectionCodec::Factory> f, Cluster& c) - : next(f), cluster(c) {} - sys::ConnectionCodec* create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, - const qpid::sys::SecuritySettings& external); - sys::ConnectionCodec* create(sys::OutputControl&, const std::string& id, - const qpid::sys::SecuritySettings& external); - }; - - ConnectionCodec(const framing::ProtocolVersion&, sys::OutputControl& out, - const std::string& logId, Cluster& c, bool catchUp, bool isLink, - const qpid::sys::SecuritySettings& external); - ~ConnectionCodec(); - - // ConnectionCodec functions. - size_t decode(const char* buffer, size_t size); - size_t encode(const char* buffer, size_t size); - bool canEncode(); - void closed(); - bool isClosed() const; - framing::ProtocolVersion getVersion() const; - void setSecureConnection(broker::SecureConnection* sc) { interceptor->setSecureConnection(sc); } - - private: - amqp_0_10::Connection codec; - boost::intrusive_ptr<cluster::Connection> interceptor; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_CONNCTIONCODEC_H*/ diff --git a/cpp/src/qpid/cluster/Cpg.cpp b/cpp/src/qpid/cluster/Cpg.cpp deleted file mode 100644 index 6e9e22a42f..0000000000 --- a/cpp/src/qpid/cluster/Cpg.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "qpid/cluster/Cpg.h" -#include "qpid/sys/Mutex.h" -#include "qpid/sys/Time.h" -#include "qpid/sys/posix/PrivatePosix.h" -#include "qpid/log/Statement.h" - -#include <vector> -#include <limits> -#include <iterator> -#include <sstream> - -#include <unistd.h> - -// This is a macro instead of a function because we don't want to -// evaluate the MSG argument unless there is an error. -#define CPG_CHECK(RESULT, MSG) \ - if ((RESULT) != CS_OK) throw Exception(errorStr((RESULT), (MSG))) - -namespace qpid { -namespace cluster { - -using namespace std; - - - -Cpg* Cpg::cpgFromHandle(cpg_handle_t handle) { - void* cpg=0; - CPG_CHECK(cpg_context_get(handle, &cpg), "Cannot get CPG instance."); - if (!cpg) throw Exception("Cannot get CPG instance."); - return reinterpret_cast<Cpg*>(cpg); -} - -// Applies the same retry-logic to all cpg calls that need it. -void Cpg::callCpg ( CpgOp & c ) { - cs_error_t result; - unsigned int snooze = 10; - for ( unsigned int nth_try = 0; nth_try < cpgRetries; ++ nth_try ) { - if ( CS_OK == (result = c.op(handle, & group))) { - break; - } - else if ( result == CS_ERR_TRY_AGAIN ) { - QPID_LOG(info, "Retrying " << c.opName ); - sys::usleep ( snooze ); - snooze *= 10; - snooze = (snooze <= maxCpgRetrySleep) ? snooze : maxCpgRetrySleep; - } - else break; // Don't retry unless CPG tells us to. - } - - if ( result != CS_OK ) - CPG_CHECK(result, c.msg(group)); -} - -// Global callback functions. -void Cpg::globalDeliver ( - cpg_handle_t handle, - const struct cpg_name *group, - uint32_t nodeid, - uint32_t pid, - void* msg, - size_t msg_len) -{ - cpgFromHandle(handle)->handler.deliver(handle, group, nodeid, pid, msg, msg_len); -} - -void Cpg::globalConfigChange( - cpg_handle_t handle, - const struct cpg_name *group, - const struct cpg_address *members, size_t nMembers, - const struct cpg_address *left, size_t nLeft, - const struct cpg_address *joined, size_t nJoined -) -{ - cpgFromHandle(handle)->handler.configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined); -} - -void Cpg::globalDeliver ( - cpg_handle_t handle, - struct cpg_name *group, - uint32_t nodeid, - uint32_t pid, - void* msg, - int msg_len) -{ - cpgFromHandle(handle)->handler.deliver(handle, group, nodeid, pid, msg, msg_len); -} - -void Cpg::globalConfigChange( - cpg_handle_t handle, - struct cpg_name *group, - struct cpg_address *members, int nMembers, - struct cpg_address *left, int nLeft, - struct cpg_address *joined, int nJoined -) -{ - cpgFromHandle(handle)->handler.configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined); -} - -int Cpg::getFd() { - int fd; - CPG_CHECK(cpg_fd_get(handle, &fd), "Cannot get CPG file descriptor"); - return fd; -} - -Cpg::Cpg(Handler& h) : IOHandle(new sys::IOHandlePrivate), handler(h), isShutdown(false) { - cpg_callbacks_t callbacks; - ::memset(&callbacks, 0, sizeof(callbacks)); - callbacks.cpg_deliver_fn = &globalDeliver; - callbacks.cpg_confchg_fn = &globalConfigChange; - - QPID_LOG(notice, "Initializing CPG"); - cs_error_t err = cpg_initialize(&handle, &callbacks); - int retries = 6; // FIXME aconway 2009-08-06: make this configurable. - while (err == CS_ERR_TRY_AGAIN && --retries) { - QPID_LOG(notice, "Re-trying CPG initialization."); - sys::sleep(5); - err = cpg_initialize(&handle, &callbacks); - } - CPG_CHECK(err, "Failed to initialize CPG."); - CPG_CHECK(cpg_context_set(handle, this), "Cannot set CPG context"); - // Note: CPG is currently unix-specific. If CPG is ported to - // windows then this needs to be refactored into - // qpid::sys::<platform> - IOHandle::impl->fd = getFd(); -} - -Cpg::~Cpg() { - try { - shutdown(); - } catch (const std::exception& e) { - QPID_LOG(error, "Error during CPG shutdown: " << e.what()); - } -} - -void Cpg::join(const std::string& name) { - group = name; - callCpg ( cpgJoinOp ); -} - -void Cpg::leave() { - callCpg ( cpgLeaveOp ); -} - - - - -bool Cpg::mcast(const iovec* iov, int iovLen) { - // Check for flow control - cpg_flow_control_state_t flowState; - CPG_CHECK(cpg_flow_control_state_get(handle, &flowState), "Cannot get CPG flow control status."); - if (flowState == CPG_FLOW_CONTROL_ENABLED) - return false; - - cs_error_t result; - do { - result = cpg_mcast_joined(handle, CPG_TYPE_AGREED, const_cast<iovec*>(iov), iovLen); - if (result != CS_ERR_TRY_AGAIN) CPG_CHECK(result, cantMcastMsg(group)); - } while(result == CS_ERR_TRY_AGAIN); - return true; -} - -void Cpg::shutdown() { - if (!isShutdown) { - QPID_LOG(debug,"Shutting down CPG"); - isShutdown=true; - - callCpg ( cpgFinalizeOp ); - } -} - -void Cpg::dispatchOne() { - CPG_CHECK(cpg_dispatch(handle,CS_DISPATCH_ONE), "Error in CPG dispatch"); -} - -void Cpg::dispatchAll() { - CPG_CHECK(cpg_dispatch(handle,CS_DISPATCH_ALL), "Error in CPG dispatch"); -} - -void Cpg::dispatchBlocking() { - CPG_CHECK(cpg_dispatch(handle,CS_DISPATCH_BLOCKING), "Error in CPG dispatch"); -} - -string Cpg::errorStr(cs_error_t err, const std::string& msg) { - std::ostringstream os; - os << msg << ": "; - switch (err) { - case CS_OK: os << "ok"; break; - case CS_ERR_LIBRARY: os << "library"; break; - case CS_ERR_TIMEOUT: os << "timeout"; break; - case CS_ERR_TRY_AGAIN: os << "try again"; break; - case CS_ERR_INVALID_PARAM: os << "invalid param"; break; - case CS_ERR_NO_MEMORY: os << "no memory"; break; - case CS_ERR_BAD_HANDLE: os << "bad handle"; break; - case CS_ERR_ACCESS: os << "access denied. You may need to set your group ID to 'ais'"; break; - case CS_ERR_NOT_EXIST: os << "not exist"; break; - case CS_ERR_EXIST: os << "exist"; break; - case CS_ERR_NOT_SUPPORTED: os << "not supported"; break; - case CS_ERR_SECURITY: os << "security"; break; - case CS_ERR_TOO_MANY_GROUPS: os << "too many groups"; break; - default: os << ": unknown cpg error " << err; - }; - os << " (" << err << ")"; - return os.str(); -} - -std::string Cpg::cantJoinMsg(const Name& group) { - return "Cannot join CPG group "+group.str(); -} - -std::string Cpg::cantFinalizeMsg(const Name& group) { - return "Cannot finalize CPG group "+group.str(); -} - -std::string Cpg::cantLeaveMsg(const Name& group) { - return "Cannot leave CPG group "+group.str(); -} - -std::string Cpg::cantMcastMsg(const Name& group) { - return "Cannot mcast to CPG group "+group.str(); -} - -MemberId Cpg::self() const { - unsigned int nodeid; - CPG_CHECK(cpg_local_get(handle, &nodeid), "Cannot get local CPG identity"); - return MemberId(nodeid, getpid()); -} - -namespace { int byte(uint32_t value, int i) { return (value >> (i*8)) & 0xff; } } - -ostream& operator<<(ostream& out, const MemberId& id) { - if (id.first) { - out << byte(id.first, 0) << "." - << byte(id.first, 1) << "." - << byte(id.first, 2) << "." - << byte(id.first, 3) - << ":"; - } - return out << id.second; -} - -ostream& operator<<(ostream& o, const ConnectionId& c) { - return o << c.first << "-" << c.second; -} - -std::string MemberId::str() const { - char s[8]; - uint32_t x; - x = htonl(first); - ::memcpy(s, &x, 4); - x = htonl(second); - ::memcpy(s+4, &x, 4); - return std::string(s,8); -} - -MemberId::MemberId(const std::string& s) { - uint32_t x; - memcpy(&x, &s[0], 4); - first = ntohl(x); - memcpy(&x, &s[4], 4); - second = ntohl(x); -} -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/Cpg.h b/cpp/src/qpid/cluster/Cpg.h deleted file mode 100644 index 1afbce8d75..0000000000 --- a/cpp/src/qpid/cluster/Cpg.h +++ /dev/null @@ -1,236 +0,0 @@ -#ifndef CPG_H -#define CPG_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/Exception.h" -#include "qpid/cluster/types.h" -#include "qpid/sys/IOHandle.h" -#include "qpid/sys/Mutex.h" - -#include <boost/scoped_ptr.hpp> - -#include <cassert> -#include <string.h> - -namespace qpid { -namespace cluster { - -/** - * Lightweight C++ interface to cpg.h operations. - * - * Manages a single CPG handle, initialized in ctor, finialzed in destructor. - * On error all functions throw Cpg::Exception. - * - */ - -class Cpg : public sys::IOHandle { - public: - struct Exception : public ::qpid::Exception { - Exception(const std::string& msg) : ::qpid::Exception(msg) {} - }; - - struct Name : public cpg_name { - Name() { length = 0; } - Name(const char* s) { copy(s, strlen(s)); } - Name(const char* s, size_t n) { copy(s,n); } - Name(const std::string& s) { copy(s.data(), s.size()); } - void copy(const char* s, size_t n) { - assert(n < CPG_MAX_NAME_LENGTH); - memcpy(value, s, n); - length=n; - } - - std::string str() const { return std::string(value, length); } - }; - - static std::string str(const cpg_name& n) { - return std::string(n.value, n.length); - } - - struct Handler { - virtual ~Handler() {}; - virtual void deliver( - cpg_handle_t /*handle*/, - const struct cpg_name *group, - uint32_t /*nodeid*/, - uint32_t /*pid*/, - void* /*msg*/, - int /*msg_len*/) = 0; - - virtual void configChange( - cpg_handle_t /*handle*/, - const struct cpg_name */*group*/, - const struct cpg_address */*members*/, int /*nMembers*/, - const struct cpg_address */*left*/, int /*nLeft*/, - const struct cpg_address */*joined*/, int /*nJoined*/ - ) = 0; - }; - - /** Open a CPG handle. - *@param handler for CPG events. - */ - Cpg(Handler&); - - /** Destructor calls shutdown if not already calledx. */ - ~Cpg(); - - /** Disconnect from CPG */ - void shutdown(); - - void dispatchOne(); - void dispatchAll(); - void dispatchBlocking(); - - void join(const std::string& group); - void leave(); - - /** Multicast to the group. NB: must not be called concurrently. - * - *@return true if the message was multi-cast, false if - * it was not sent due to flow control. - */ - bool mcast(const iovec* iov, int iovLen); - - cpg_handle_t getHandle() const { return handle; } - - MemberId self() const; - - int getFd(); - - private: - - // Maximum number of retries for cog functions that can tell - // us to "try again later". - static const unsigned int cpgRetries = 5; - - // Don't let sleep-time between cpg retries to go above 0.1 second. - static const unsigned int maxCpgRetrySleep = 100000; - - - // Base class for the Cpg operations that need retry capability. - struct CpgOp { - std::string opName; - - CpgOp ( std::string opName ) - : opName(opName) { } - - virtual cs_error_t op ( cpg_handle_t handle, struct cpg_name * ) = 0; - virtual std::string msg(const Name&) = 0; - virtual ~CpgOp ( ) { } - }; - - - struct CpgJoinOp : public CpgOp { - CpgJoinOp ( ) - : CpgOp ( std::string("cpg_join") ) { } - - cs_error_t op(cpg_handle_t handle, struct cpg_name * group) { - return cpg_join ( handle, group ); - } - - std::string msg(const Name& name) { return cantJoinMsg(name); } - }; - - struct CpgLeaveOp : public CpgOp { - CpgLeaveOp ( ) - : CpgOp ( std::string("cpg_leave") ) { } - - cs_error_t op(cpg_handle_t handle, struct cpg_name * group) { - return cpg_leave ( handle, group ); - } - - std::string msg(const Name& name) { return cantLeaveMsg(name); } - }; - - struct CpgFinalizeOp : public CpgOp { - CpgFinalizeOp ( ) - : CpgOp ( std::string("cpg_finalize") ) { } - - cs_error_t op(cpg_handle_t handle, struct cpg_name *) { - return cpg_finalize ( handle ); - } - - std::string msg(const Name& name) { return cantFinalizeMsg(name); } - }; - - // This fn standardizes retry policy across all Cpg ops that need it. - void callCpg ( CpgOp & ); - - CpgJoinOp cpgJoinOp; - CpgLeaveOp cpgLeaveOp; - CpgFinalizeOp cpgFinalizeOp; - - static std::string errorStr(cs_error_t err, const std::string& msg); - static std::string cantJoinMsg(const Name&); - static std::string cantLeaveMsg(const Name&); - static std::string cantMcastMsg(const Name&); - static std::string cantFinalizeMsg(const Name&); - - static Cpg* cpgFromHandle(cpg_handle_t); - - // New versions for corosync 1.0 and higher - static void globalDeliver( - cpg_handle_t handle, - const struct cpg_name *group, - uint32_t nodeid, - uint32_t pid, - void* msg, - size_t msg_len); - - static void globalConfigChange( - cpg_handle_t handle, - const struct cpg_name *group, - const struct cpg_address *members, size_t nMembers, - const struct cpg_address *left, size_t nLeft, - const struct cpg_address *joined, size_t nJoined - ); - - // Old versions for openais - static void globalDeliver( - cpg_handle_t handle, - struct cpg_name *group, - uint32_t nodeid, - uint32_t pid, - void* msg, - int msg_len); - - static void globalConfigChange( - cpg_handle_t handle, - struct cpg_name *group, - struct cpg_address *members, int nMembers, - struct cpg_address *left, int nLeft, - struct cpg_address *joined, int nJoined - ); - - cpg_handle_t handle; - Handler& handler; - bool isShutdown; - Name group; - sys::Mutex dispatchLock; -}; - -inline bool operator==(const cpg_name& a, const cpg_name& b) { - return a.length==b.length && strncmp(a.value, b.value, a.length) == 0; -} -inline bool operator!=(const cpg_name& a, const cpg_name& b) { return !(a == b); } - -}} // namespace qpid::cluster - -#endif /*!CPG_H*/ diff --git a/cpp/src/qpid/cluster/CredentialsExchange.cpp b/cpp/src/qpid/cluster/CredentialsExchange.cpp deleted file mode 100644 index 416a3636e9..0000000000 --- a/cpp/src/qpid/cluster/CredentialsExchange.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "CredentialsExchange.h" -#include "Cluster.h" -#include "qpid/broker/ConnectionState.h" -#include "qpid/framing/reply_exceptions.h" -#include "qpid/sys/Time.h" - -namespace qpid { -namespace cluster { - -using namespace std; - -const string CredentialsExchange::NAME=("qpid.cluster-credentials"); - -namespace { -const string ANONYMOUS_MECH("ANONYMOUS"); -const string ANONYMOUS_USER("anonymous"); - -string effectiveUserId(const string& username, const string& mechanism) { - if (mechanism == ANONYMOUS_MECH && username.empty()) - return ANONYMOUS_USER; - else - return username; -} -} - -CredentialsExchange::CredentialsExchange(Cluster& cluster) - : broker::Exchange(NAME, &cluster), - username(effectiveUserId(cluster.getSettings().username, - cluster.getSettings().mechanism)), - timeout(120*sys::TIME_SEC), - authenticate(cluster.getBroker().getOptions().auth) -{} - -static const string anonymous("anonymous"); - -bool CredentialsExchange::check(MemberId member) { - sys::Mutex::ScopedLock l(lock); - Map::iterator i = map.find(member); - if (i == map.end()) return false; - bool valid = (sys::Duration(i->second, sys::AbsTime::now()) < timeout); - map.erase(i); - return valid; -} - -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()); - if (authenticate && !connection->isAuthenticatedUser(username)) - throw framing::UnauthorizedAccessException( - QPID_MSG("Unauthorized user " << connection->getUserId() << " for " << NAME - << ", should be " << username)); - if (!args || !args->isSet(NAME)) - throw framing::InvalidArgumentException( - QPID_MSG("Invalid message received by " << NAME)); - MemberId member(args->getAsUInt64(NAME)); - map[member] = sys::AbsTime::now(); -} - -string CredentialsExchange::getType() const { return NAME; } - -namespace { -void throwIllegal() { - throw framing::NotAllowedException( - QPID_MSG("Illegal use of " << CredentialsExchange::NAME+" exchange")); -} -} - -bool CredentialsExchange::bind(boost::shared_ptr<broker::Queue> , const string& /*routingKey*/, const framing::FieldTable* ) { throwIllegal(); return false; } -bool CredentialsExchange::unbind(boost::shared_ptr<broker::Queue> , const string& /*routingKey*/, const framing::FieldTable* ) { throwIllegal(); return false; } -bool CredentialsExchange::isBound(boost::shared_ptr<broker::Queue>, const string* const /*routingKey*/, const framing::FieldTable* const ) { throwIllegal(); return false; } - - -}} // Namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/CredentialsExchange.h b/cpp/src/qpid/cluster/CredentialsExchange.h deleted file mode 100644 index 74cf8350a6..0000000000 --- a/cpp/src/qpid/cluster/CredentialsExchange.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef QPID_CLUSTER_CREDENTIALSEXCHANGE_H -#define QPID_CLUSTER_CREDENTIALSEXCHANGE_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 "types.h" -#include <qpid/broker/Exchange.h> -#include <qpid/sys/Mutex.h> -#include <qpid/sys/Time.h> -#include <string> -#include <map> - -namespace qpid { -namespace cluster { - -class Cluster; - -/** - * New members joining the cluster send their identity information to this - * exchange to prove they are authenticated as the cluster user. - * The exchange rejects messages that are not properly authenticated - */ -class CredentialsExchange : public broker::Exchange -{ - public: - static const std::string NAME; - - CredentialsExchange(Cluster&); - - /** Check if this member has credentials. The credentials are deleted. */ - 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); - - // Exchange overrides - std::string getType() const; - 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); - - private: - typedef std::map<MemberId, sys::AbsTime> Map; - sys::Mutex lock; - Map map; - std::string username; - sys::Duration timeout; - bool authenticate; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_CREDENTIALSEXCHANGE_H*/ diff --git a/cpp/src/qpid/cluster/Decoder.cpp b/cpp/src/qpid/cluster/Decoder.cpp deleted file mode 100644 index 23ba372d78..0000000000 --- a/cpp/src/qpid/cluster/Decoder.cpp +++ /dev/null @@ -1,65 +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/cluster/Decoder.h" -#include "qpid/cluster/EventFrame.h" -#include "qpid/framing/ClusterConnectionDeliverCloseBody.h" -#include "qpid/framing/Buffer.h" -#include "qpid/framing/AMQFrame.h" - - -namespace qpid { -namespace cluster { - -void Decoder::decode(const EventHeader& eh, const char* data) { - sys::Mutex::ScopedLock l(lock); - assert(eh.getType() == DATA); // Only handle connection data events. - const char* cp = static_cast<const char*>(data); - framing::Buffer buf(const_cast<char*>(cp), eh.getSize()); - framing::FrameDecoder& decoder = map[eh.getConnectionId()]; - if (decoder.decode(buf)) { // Decoded a frame - framing::AMQFrame frame(decoder.getFrame()); - while (decoder.decode(buf)) { - callback(EventFrame(eh, frame)); - frame = decoder.getFrame(); - } - // Set read-credit on the last frame ending in this event. - // Credit will be given when this frame is processed. - callback(EventFrame(eh, frame, 1)); - } - else { - // We must give 1 unit read credit per event. - // This event does not complete any frames so - // send an empty frame with the read credit. - callback(EventFrame(eh, framing::AMQFrame(), 1)); - } -} - -void Decoder::erase(const ConnectionId& c) { - sys::Mutex::ScopedLock l(lock); - map.erase(c); -} - -framing::FrameDecoder& Decoder::get(const ConnectionId& c) { - sys::Mutex::ScopedLock l(lock); - return map[c]; -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/Decoder.h b/cpp/src/qpid/cluster/Decoder.h deleted file mode 100644 index 3b5ada4a81..0000000000 --- a/cpp/src/qpid/cluster/Decoder.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef QPID_CLUSTER_DECODER_H -#define QPID_CLUSTER_DECODER_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/cluster/types.h" -#include "qpid/framing/FrameDecoder.h" -#include "qpid/sys/Mutex.h" -#include <boost/function.hpp> -#include <map> - -namespace qpid { -namespace cluster { - -struct EventFrame; -class EventHeader; - -/** - * A map of decoders for connections. - */ -class Decoder -{ - public: - typedef boost::function<void(const EventFrame&)> FrameHandler; - - Decoder(FrameHandler fh) : callback(fh) {} - void decode(const EventHeader& eh, const char* data); - void erase(const ConnectionId&); - framing::FrameDecoder& get(const ConnectionId& c); - - private: - typedef std::map<ConnectionId, framing::FrameDecoder> Map; - sys::Mutex lock; - Map map; - void process(const EventFrame&); - FrameHandler callback; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_DECODER_H*/ diff --git a/cpp/src/qpid/cluster/Dispatchable.h b/cpp/src/qpid/cluster/Dispatchable.h deleted file mode 100644 index e7f0df4218..0000000000 --- a/cpp/src/qpid/cluster/Dispatchable.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef QPID_CLUSTER_DISPATCHABLE_H -#define QPID_CLUSTER_DISPATCHABLE_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. - * - */ - -namespace qpid { -namespace cluster { - -/** - * Interface for classes that have some "events" that need dispatching - * in a thread. - */ -class Dispatchable -{ - public: - virtual ~Dispatchable() {} - - /** Dispatch one event in current thread. */ - virtual void dispatchOne() = 0; - /** Dispatch all available events, don't block. */ - virtual void dispatchAll() = 0; - /** Blocking loop to dispatch cluster events */ - virtual void dispatchBlocking() = 0; - - /** Wait for at least one event, then dispatch all available events. - * Don't block. Useful for tests. - */ - virtual void dispatchSome() { dispatchOne(); dispatchAll(); } - -}; - -}} // namespace qpid::cluster - - - -#endif /*!QPID_CLUSTER_DISPATCHABLE_H*/ diff --git a/cpp/src/qpid/cluster/ErrorCheck.cpp b/cpp/src/qpid/cluster/ErrorCheck.cpp deleted file mode 100644 index be671c0f48..0000000000 --- a/cpp/src/qpid/cluster/ErrorCheck.cpp +++ /dev/null @@ -1,155 +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/cluster/ErrorCheck.h" -#include "qpid/cluster/EventFrame.h" -#include "qpid/cluster/ClusterMap.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/framing/ClusterErrorCheckBody.h" -#include "qpid/framing/ClusterConfigChangeBody.h" -#include "qpid/log/Statement.h" - -#include <algorithm> - -namespace qpid { -namespace cluster { - -using namespace std; -using namespace framing; -using namespace framing::cluster; - -ErrorCheck::ErrorCheck(Cluster& c) - : cluster(c), mcast(c.getMulticast()), frameSeq(0), type(ERROR_TYPE_NONE), connection(0) -{} - -void ErrorCheck::error( - Connection& c, ErrorType t, framing::SequenceNumber seq, const MemberSet& ms, - const std::string& msg) -{ - // Detected a local error, inform cluster and set error state. - assert(t != ERROR_TYPE_NONE); // Must be an error. - assert(type == ERROR_TYPE_NONE); // Can't be called when already in an error state. - type = t; - unresolved = ms; - frameSeq = seq; - connection = &c; - message = msg; - QPID_LOG(debug, cluster<< (type == ERROR_TYPE_SESSION ? " channel" : " connection") - << " error " << frameSeq << " on " << c - << " must be resolved with: " << unresolved - << ": " << message); - mcast.mcastControl( - ClusterErrorCheckBody(ProtocolVersion(), type, frameSeq), cluster.getId()); - // If there are already frames queued up by a previous error, review - // them with respect to this new error. - for (FrameQueue::iterator i = frames.begin(); i != frames.end(); i = review(i)) - ; -} - -void ErrorCheck::delivered(const EventFrame& e) { - frames.push_back(e); - review(frames.end()-1); -} - -// Review a frame in the queue with respect to the current error. -ErrorCheck::FrameQueue::iterator ErrorCheck::review(const FrameQueue::iterator& i) { - FrameQueue::iterator next = i+1; - if(!isUnresolved() || !i->frame.getBody() || !i->frame.getMethod()) - return next; // Only interested in control frames while unresolved. - const AMQMethodBody* method = i->frame.getMethod(); - if (method->isA<const ClusterErrorCheckBody>()) { - const ClusterErrorCheckBody* errorCheck = - static_cast<const ClusterErrorCheckBody*>(method); - - if (errorCheck->getFrameSeq() == frameSeq) { // Addresses current error - next = frames.erase(i); // Drop matching error check controls - if (errorCheck->getType() < type) { // my error is worse than his - QPID_LOG(critical, cluster - << " local error " << frameSeq << " did not occur on member " - << i->getMemberId() - << ": " << message); - throw Exception( - QPID_MSG("local error did not occur on all cluster members " << ": " << message)); - } - else { // his error is worse/same as mine. - QPID_LOG(debug, cluster << " error " << frameSeq - << " resolved with " << i->getMemberId()); - unresolved.erase(i->getMemberId()); - checkResolved(); - } - } - else if (errorCheck->getFrameSeq() < frameSeq && errorCheck->getType() != NONE - && i->connectionId.getMember() != cluster.getId()) - { - // This error occured before the current error so we - // have processed past it. - next = frames.erase(i); // Drop the error check control - respondNone(i->connectionId.getMember(), errorCheck->getType(), - errorCheck->getFrameSeq()); - } - // if errorCheck->getFrameSeq() > frameSeq then leave it in the queue. - } - else if (method->isA<const ClusterConfigChangeBody>()) { - const ClusterConfigChangeBody* configChange = - static_cast<const ClusterConfigChangeBody*>(method); - if (configChange) { - MemberSet members(decodeMemberSet(configChange->getMembers())); - QPID_LOG(debug, cluster << " apply config change to error " - << frameSeq << ": " << members); - MemberSet intersect; - set_intersection(members.begin(), members.end(), - unresolved.begin(), unresolved.end(), - inserter(intersect, intersect.begin())); - unresolved.swap(intersect); - checkResolved(); - } - } - return next; -} - -void ErrorCheck::checkResolved() { - if (unresolved.empty()) { // No more potentially conflicted members, we're clear. - type = ERROR_TYPE_NONE; - QPID_LOG(debug, cluster << " error " << frameSeq << " resolved."); - } - else - QPID_LOG(debug, cluster << " error " << frameSeq - << " must be resolved with " << unresolved); -} - -EventFrame ErrorCheck::getNext() { - assert(canProcess()); - EventFrame e(frames.front()); - frames.pop_front(); - return e; -} - -void ErrorCheck::respondNone(const MemberId& from, uint8_t type, framing::SequenceNumber frameSeq) { - // Don't respond to non-errors or to my own errors. - if (type == ERROR_TYPE_NONE || from == cluster.getId()) - return; - QPID_LOG(debug, cluster << " error " << frameSeq << " did not occur locally."); - mcast.mcastControl( - ClusterErrorCheckBody(ProtocolVersion(), ERROR_TYPE_NONE, frameSeq), - cluster.getId() - ); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/ErrorCheck.h b/cpp/src/qpid/cluster/ErrorCheck.h deleted file mode 100644 index a417b2ec25..0000000000 --- a/cpp/src/qpid/cluster/ErrorCheck.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef QPID_CLUSTER_ERRORCHECK_H -#define QPID_CLUSTER_ERRORCHECK_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/cluster/MemberSet.h" -#include "qpid/cluster/Multicaster.h" -#include "qpid/framing/enum.h" -#include "qpid/framing/SequenceNumber.h" -#include <boost/function.hpp> -#include <deque> -#include <set> - -namespace qpid { -namespace cluster { - -struct EventFrame; -class Cluster; -class Multicaster; -class Connection; - -/** - * Error checking logic. - * - * When an error occurs queue up frames until we can determine if all - * nodes experienced the error. If not, we shut down. - */ -class ErrorCheck -{ - public: - typedef framing::cluster::ErrorType ErrorType; - typedef framing::SequenceNumber SequenceNumber; - - ErrorCheck(Cluster&); - - /** A local error has occured */ - void error(Connection&, ErrorType, SequenceNumber frameSeq, const MemberSet&, - const std::string& msg); - - /** Called when a frame is delivered */ - void delivered(const EventFrame&); - - /**@pre canProcess **/ - EventFrame getNext(); - - bool canProcess() const { return type == NONE && !frames.empty(); } - - bool isUnresolved() const { return type != NONE; } - - /** Respond to an error check saying we had no error. */ - void respondNone(const MemberId&, uint8_t type, SequenceNumber frameSeq); - - private: - static const ErrorType NONE = framing::cluster::ERROR_TYPE_NONE; - typedef std::deque<EventFrame> FrameQueue; - FrameQueue::iterator review(const FrameQueue::iterator&); - void checkResolved(); - - Cluster& cluster; - Multicaster& mcast; - FrameQueue frames; - MemberSet unresolved; - SequenceNumber frameSeq; - ErrorType type; - Connection* connection; - std::string message; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_ERRORCHECK_H*/ diff --git a/cpp/src/qpid/cluster/Event.cpp b/cpp/src/qpid/cluster/Event.cpp deleted file mode 100644 index da2bc89d8c..0000000000 --- a/cpp/src/qpid/cluster/Event.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/cluster/types.h" -#include "qpid/cluster/Event.h" -#include "qpid/cluster/Cpg.h" -#include "qpid/framing/Buffer.h" -#include "qpid/framing/AMQFrame.h" -#include "qpid/RefCountedBuffer.h" -#include "qpid/assert.h" -#include <ostream> -#include <iterator> -#include <algorithm> - -namespace qpid { -namespace cluster { - -using framing::Buffer; -using framing::AMQFrame; - -const size_t EventHeader::HEADER_SIZE = - sizeof(uint8_t) + // type - sizeof(uint64_t) + // connection pointer only, CPG provides member ID. - sizeof(uint32_t) // payload size - ; - -EventHeader::EventHeader(EventType t, const ConnectionId& c, size_t s) - : type(t), connectionId(c), size(s) {} - - -Event::Event() {} - -Event::Event(EventType t, const ConnectionId& c, size_t s) - : EventHeader(t,c,s), store(RefCountedBuffer::create(s+HEADER_SIZE)) -{} - -void EventHeader::decode(const MemberId& m, framing::Buffer& buf) { - QPID_ASSERT(buf.available() >= HEADER_SIZE); - type = (EventType)buf.getOctet(); - QPID_ASSERT(type == DATA || type == CONTROL); - connectionId = ConnectionId(m, buf.getLongLong()); - size = buf.getLong(); -} - -Event Event::decodeCopy(const MemberId& m, framing::Buffer& buf) { - Event e; - e.decode(m, buf); // Header - QPID_ASSERT(buf.available() >= e.size); - e.store = RefCountedBuffer::create(e.size + HEADER_SIZE); - memcpy(e.getData(), buf.getPointer() + buf.getPosition(), e.size); - return e; -} - -Event Event::control(const framing::AMQFrame& f, const ConnectionId& cid) { - Event e(CONTROL, cid, f.encodedSize()); - Buffer buf(e); - f.encode(buf); - return e; -} - -Event Event::control(const framing::AMQBody& body, const ConnectionId& cid) { - return control(framing::AMQFrame(body), cid); -} - -iovec Event::toIovec() const { - encodeHeader(); - iovec iov = { const_cast<char*>(getStore()), getStoreSize() }; - return iov; -} - -void EventHeader::encode(Buffer& b) const { - b.putOctet(type); - b.putLongLong(connectionId.getNumber()); - b.putLong(size); -} - -// Encode my header in my buffer. -void Event::encodeHeader () const { - Buffer b(const_cast<char*>(getStore()), HEADER_SIZE); - encode(b); - assert(b.getPosition() == HEADER_SIZE); -} - -Event::operator Buffer() const { - return Buffer(const_cast<char*>(getData()), getSize()); -} - -const AMQFrame& Event::getFrame() const { - assert(type == CONTROL); - if (!frame.getBody()) { - Buffer buf(*this); - QPID_ASSERT(frame.decode(buf)); - } - return frame; -} - -static const char* EVENT_TYPE_NAMES[] = { "data", "control" }; - -std::ostream& operator<< (std::ostream& o, EventType t) { - return o << EVENT_TYPE_NAMES[t]; -} - -std::ostream& operator<< (std::ostream& o, const EventHeader& e) { - return o << "Event[" << e.getConnectionId() << " " << e.getType() - << " " << e.getSize() << " bytes]"; -} - -std::ostream& operator<< (std::ostream& o, const Event& e) { - o << "Event[" << e.getConnectionId() << " "; - if (e.getType() == CONTROL) - o << e.getFrame(); - else - o << " data " << e.getSize() << " bytes"; - return o << "]"; -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/Event.h b/cpp/src/qpid/cluster/Event.h deleted file mode 100644 index 13283edff7..0000000000 --- a/cpp/src/qpid/cluster/Event.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef QPID_CLUSTER_EVENT_H -#define QPID_CLUSTER_EVENT_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/cluster/types.h" -#include "qpid/BufferRef.h" -#include "qpid/framing/AMQFrame.h" -#include <sys/uio.h> // For iovec -#include <iosfwd> - -#include "qpid/cluster/types.h" - -namespace qpid { - -namespace framing { -class AMQBody; -class AMQFrame; -class Buffer; -} - -namespace cluster { - -/** Header data for a multicast event */ -class EventHeader { - public: - EventHeader(EventType t=DATA, const ConnectionId& c=ConnectionId(), size_t size=0); - void decode(const MemberId& m, framing::Buffer&); - void encode(framing::Buffer&) const; - - EventType getType() const { return type; } - ConnectionId getConnectionId() const { return connectionId; } - MemberId getMemberId() const { return connectionId.getMember(); } - - /** Size of payload data, excluding header. */ - size_t getSize() const { return size; } - /** Size of header + payload. */ - size_t getStoreSize() const { return size + HEADER_SIZE; } - - bool isCluster() const { return connectionId.getNumber() == 0; } - bool isConnection() const { return connectionId.getNumber() != 0; } - bool isControl() const { return type == CONTROL; } - - protected: - static const size_t HEADER_SIZE; - - EventType type; - ConnectionId connectionId; - size_t size; -}; - -/** - * Events are sent to/received from the cluster. - * Refcounted so they can be stored on queues. - */ -class Event : public EventHeader { - public: - Event(); - /** Create an event with a buffer that can hold size bytes plus an event header. */ - Event(EventType t, const ConnectionId& c, size_t); - - /** Create an event copied from delivered data. */ - static Event decodeCopy(const MemberId& m, framing::Buffer&); - - /** Create a control event. */ - static Event control(const framing::AMQBody&, const ConnectionId&); - - /** Create a control event. */ - static Event control(const framing::AMQFrame&, const ConnectionId&); - - // Data excluding header. - char* getData() { return store.begin() + HEADER_SIZE; } - const char* getData() const { return store.begin() + HEADER_SIZE; } - - // Store including header - char* getStore() { return store.begin(); } - const char* getStore() const { return store.begin(); } - - const framing::AMQFrame& getFrame() const; - - operator framing::Buffer() const; - - iovec toIovec() const; - - private: - void encodeHeader() const; - - BufferRef store; - mutable framing::AMQFrame frame; -}; - -std::ostream& operator << (std::ostream&, const Event&); -std::ostream& operator << (std::ostream&, const EventHeader&); - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_EVENT_H*/ diff --git a/cpp/src/qpid/cluster/EventFrame.cpp b/cpp/src/qpid/cluster/EventFrame.cpp deleted file mode 100644 index 5fbe1fe57c..0000000000 --- a/cpp/src/qpid/cluster/EventFrame.cpp +++ /dev/null @@ -1,41 +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/cluster/EventFrame.h" -#include "qpid/cluster/Connection.h" - -namespace qpid { -namespace cluster { - -EventFrame::EventFrame() {} - -EventFrame::EventFrame(const EventHeader& e, const framing::AMQFrame& f, int rc) - : connectionId(e.getConnectionId()), frame(f), readCredit(rc), type(e.getType()) -{} - -std::ostream& operator<<(std::ostream& o, const EventFrame& e) { - if (e.frame.getBody()) o << e.frame; - else o << "null-frame"; - o << " " << e.type << " " << e.connectionId; - if (e.readCredit) o << " read-credit=" << e.readCredit; - return o; -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/EventFrame.h b/cpp/src/qpid/cluster/EventFrame.h deleted file mode 100644 index 6b702a9bf8..0000000000 --- a/cpp/src/qpid/cluster/EventFrame.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef QPID_CLUSTER_EVENTFRAME_H -#define QPID_CLUSTER_EVENTFRAME_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/cluster/types.h" -#include "qpid/cluster/Event.h" -#include "qpid/framing/AMQFrame.h" -#include <boost/intrusive_ptr.hpp> -#include <iosfwd> - -namespace qpid { -namespace cluster { - -/** - * A frame decoded from an Event. - */ -struct EventFrame -{ - public: - EventFrame(); - - EventFrame(const EventHeader& e, const framing::AMQFrame& f, int rc=0); - - bool isCluster() const { return connectionId.getNumber() == 0; } - bool isConnection() const { return connectionId.getNumber() != 0; } - bool isLastInEvent() const { return readCredit; } - MemberId getMemberId() const { return connectionId.getMember(); } - - - ConnectionId connectionId; - framing::AMQFrame frame; - int readCredit; ///< last frame in an event, give credit when processed. - EventType type; -}; - -std::ostream& operator<<(std::ostream& o, const EventFrame& e); - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_EVENTFRAME_H*/ diff --git a/cpp/src/qpid/cluster/FailoverExchange.cpp b/cpp/src/qpid/cluster/FailoverExchange.cpp deleted file mode 100644 index 87202a887c..0000000000 --- a/cpp/src/qpid/cluster/FailoverExchange.cpp +++ /dev/null @@ -1,109 +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/cluster/FailoverExchange.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/DeliverableMessage.h" -#include "qpid/broker/Queue.h" -#include "qpid/framing/MessageProperties.h" -#include "qpid/framing/AMQFrame.h" -#include "qpid/framing/AMQHeaderBody.h" -#include "qpid/framing/MessageTransferBody.h" -#include "qpid/log/Statement.h" -#include "qpid/framing/Array.h" -#include "qpid/UrlArray.h" -#include <boost/bind.hpp> -#include <algorithm> - -namespace qpid { -namespace cluster { -using namespace std; - -using namespace broker; -using namespace framing; - -const string FailoverExchange::typeName("amq.failover"); - -FailoverExchange::FailoverExchange(management::Manageable* parent, Broker* b) - : Exchange(typeName, parent, b ), ready(false) -{ - if (mgmtExchange != 0) - mgmtExchange->set_type(typeName); -} - -void FailoverExchange::setUrls(const vector<Url>& u) { - Lock l(lock); - urls = u; -} - -void FailoverExchange::updateUrls(const vector<Url>& u) { - Lock l(lock); - urls=u; - if (ready && !urls.empty()) { - std::for_each(queues.begin(), queues.end(), - boost::bind(&FailoverExchange::sendUpdate, this, _1)); - } -} - -string FailoverExchange::getType() const { return typeName; } - -bool FailoverExchange::bind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) { - Lock l(lock); - if (ready) sendUpdate(queue); - return queues.insert(queue).second; -} - -bool FailoverExchange::unbind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) { - Lock l(lock); - return queues.erase(queue); -} - -bool FailoverExchange::isBound(Queue::shared_ptr queue, const string* const, const framing::FieldTable*) { - Lock l(lock); - return queues.find(queue) != queues.end(); -} - -void FailoverExchange::route(Deliverable&) { - QPID_LOG(warning, "Message received by exchange " << typeName << " ignoring"); -} - -void FailoverExchange::sendUpdate(const Queue::shared_ptr& queue) { - // Called with lock held. - if (urls.empty()) return; - framing::Array array = vectorToUrlArray(urls); - const ProtocolVersion v; - boost::intrusive_ptr<Message> msg(new Message); - AMQFrame command(MessageTransferBody(v, typeName, 1, 0)); - command.setLastSegment(false); - msg->getFrames().append(command); - AMQHeaderBody header; - header.get<MessageProperties>(true)->setContentLength(0); - header.get<MessageProperties>(true)->getApplicationHeaders().setArray(typeName, array); - AMQFrame headerFrame(header); - headerFrame.setFirstSegment(false); - msg->getFrames().append(headerFrame); - DeliverableMessage(msg).deliverTo(queue); -} - -void FailoverExchange::setReady() { - ready = true; -} - -}} // namespace cluster diff --git a/cpp/src/qpid/cluster/FailoverExchange.h b/cpp/src/qpid/cluster/FailoverExchange.h deleted file mode 100644 index 5ac734a7ac..0000000000 --- a/cpp/src/qpid/cluster/FailoverExchange.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef QPID_CLUSTER_FAILOVEREXCHANGE_H -#define QPID_CLUSTER_FAILOVEREXCHANGE_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/Exchange.h" -#include "qpid/broker/DeliverableMessage.h" -#include "qpid/Url.h" - -#include <vector> -#include <set> - -namespace qpid { -namespace cluster { - -/** - * Failover exchange provides failover host list, as specified in AMQP 0-10. - */ -class FailoverExchange : public broker::Exchange -{ - public: - static const std::string typeName; - - FailoverExchange(management::Manageable* parent, broker::Broker* b); - - /** Set the URLs but don't send an update.*/ - void setUrls(const std::vector<Url>&); - /** Set the URLs and send an update.*/ - void updateUrls(const std::vector<Url>&); - /** Flag the failover exchange as ready to generate updates (caught up) */ - void setReady(); - - // Exchange overrides - std::string getType() const; - 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); - - private: - void sendUpdate(const boost::shared_ptr<broker::Queue>&); - - typedef sys::Mutex::ScopedLock Lock; - typedef std::vector<Url> Urls; - typedef std::set<boost::shared_ptr<broker::Queue> > Queues; - - sys::Mutex lock; - Urls urls; - Queues queues; - bool ready; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_FAILOVEREXCHANGE_H*/ diff --git a/cpp/src/qpid/cluster/InitialStatusMap.cpp b/cpp/src/qpid/cluster/InitialStatusMap.cpp deleted file mode 100644 index fc53d1076b..0000000000 --- a/cpp/src/qpid/cluster/InitialStatusMap.cpp +++ /dev/null @@ -1,238 +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 "InitialStatusMap.h" -#include "StoreStatus.h" -#include "qpid/log/Statement.h" -#include "qpid/UrlArray.h" -#include <algorithm> -#include <vector> -#include <boost/bind.hpp> - -namespace qpid { -namespace cluster { - -using namespace std; -using namespace framing::cluster; -using namespace framing; -using boost::optional; - -InitialStatusMap::InitialStatusMap(const MemberId& self_, size_t size_) - : self(self_), completed(), resendNeeded(), size(size_) -{} - -void InitialStatusMap::configChange(const MemberSet& members) { - resendNeeded = false; - bool wasComplete = isComplete(); - if (firstConfig.empty()) firstConfig = members; - MemberSet::const_iterator i = members.begin(); - Map::iterator j = map.begin(); - while (i != members.end() || j != map.end()) { - if (i == members.end()) { // j not in members, member left - firstConfig.erase(j->first); - Map::iterator k = j++; - map.erase(k); - } - else if (j == map.end()) { // i not in map, member joined - resendNeeded = true; - map[*i] = optional<Status>(); - ++i; - } - else if (*i < j->first) { // i not in map, member joined - resendNeeded = true; - map[*i] = optional<Status>(); - ++i; - } - else if (*i > j->first) { // j not in members, member left - firstConfig.erase(j->first); - Map::iterator k = j++; - map.erase(k); - } - else { - i++; j++; - } - } - if (resendNeeded) { // Clear all status - for (Map::iterator i = map.begin(); i != map.end(); ++i) - i->second = optional<Status>(); - } - completed = isComplete() && !wasComplete; // Set completed on the transition. -} - -void InitialStatusMap::received(const MemberId& m, const Status& s){ - bool wasComplete = isComplete(); - map[m] = s; - completed = isComplete() && !wasComplete; // Set completed on the transition. -} - -bool InitialStatusMap::notInitialized(const Map::value_type& v) { - return !v.second; -} - -bool InitialStatusMap::isComplete() const { - return !map.empty() && find_if(map.begin(), map.end(), ¬Initialized) == map.end(); -} - -bool InitialStatusMap::transitionToComplete() { - return completed; -} - -bool InitialStatusMap::isResendNeeded() { - bool ret = resendNeeded; - resendNeeded = false; - return ret; -} - -bool InitialStatusMap::isActiveEntry(const Map::value_type& v) { - return v.second && v.second->getActive(); -} - -bool InitialStatusMap::hasStore(const Map::value_type& v) { - return v.second && - (v.second->getStoreState() == STORE_STATE_CLEAN_STORE || - v.second->getStoreState() == STORE_STATE_DIRTY_STORE); -} - -bool InitialStatusMap::isActive() { - assert(isComplete()); - return (find_if(map.begin(), map.end(), &isActiveEntry) != map.end()); -} - -bool InitialStatusMap::isUpdateNeeded() { - assert(isComplete()); - // We need an update if there are any active members. - if (isActive()) return true; - - // Otherwise it depends on store status, get my own status: - Map::iterator me = map.find(self); - assert(me != map.end()); - assert(me->second); - switch (me->second->getStoreState()) { - case STORE_STATE_NO_STORE: - case STORE_STATE_EMPTY_STORE: - // If anybody has a store then we need an update. - return find_if(map.begin(), map.end(), &hasStore) != map.end(); - case STORE_STATE_DIRTY_STORE: return true; - case STORE_STATE_CLEAN_STORE: return false; // Use our own store - } - return false; -} - -MemberSet InitialStatusMap::getElders() const { - assert(isComplete()); - MemberSet elders; - for (MemberSet::const_iterator i = firstConfig.begin(); i != firstConfig.end(); ++i) { - // *i is in my first config, so a potential elder. - if (*i == self) continue; // Not my own elder - Map::const_iterator j = map.find(*i); - assert(j != map.end()); - assert(j->second); - const Status& s = *j->second; - // If I'm not in i's first config then i is older than me. - // Otherwise we were born in the same configuration so use - // member ID to break the tie. - MemberSet iFirstConfig = decodeMemberSet(s.getFirstConfig()); - if (iFirstConfig.find(self) == iFirstConfig.end() || *i > self) - elders.insert(*i); - } - return elders; -} - -// Get cluster ID from an active member or the youngest newcomer. -Uuid InitialStatusMap::getClusterId() { - assert(isComplete()); - assert(!map.empty()); - Map::iterator i = find_if(map.begin(), map.end(), &isActiveEntry); - if (i != map.end()) - return i->second->getClusterId(); // An active member - else - return map.begin()->second->getClusterId(); // Youngest newcomer in node-id order -} - -void checkId(Uuid& expect, const Uuid& actual, const string& msg) { - if (!expect) expect = actual; - assert(expect); - if (expect != actual) - throw Exception(msg); -} - -void InitialStatusMap::checkConsistent() { - assert(isComplete()); - int clean = 0; - int dirty = 0; - int empty = 0; - int none = 0; - int active = 0; - Uuid clusterId; - Uuid shutdownId; - - bool initialCluster = !isActive(); - for (Map::iterator i = map.begin(); i != map.end(); ++i) { - assert(i->second); - if (i->second->getActive()) ++active; - switch (i->second->getStoreState()) { - case STORE_STATE_NO_STORE: ++none; break; - case STORE_STATE_EMPTY_STORE: ++empty; break; - case STORE_STATE_DIRTY_STORE: - ++dirty; - checkId(clusterId, i->second->getClusterId(), - "Cluster-ID mismatch. Stores belong to different clusters."); - break; - case STORE_STATE_CLEAN_STORE: - ++clean; - checkId(clusterId, i->second->getClusterId(), - "Cluster-ID mismatch. Stores belong to different clusters."); - // Only need shutdownId to match if we are in an initially forming cluster. - if (initialCluster) - checkId(shutdownId, i->second->getShutdownId(), - "Shutdown-ID mismatch. Stores were not shut down together"); - break; - } - } - // Can't mix transient and persistent members. - if (none && (clean+dirty+empty)) - throw Exception("Mixing transient and persistent brokers in a cluster"); - - if (map.size() >= size) { - // All initial members are present. If there are no active - // members and there are dirty stores there must be at least - // one clean store. - if (!active && dirty && !clean) - throw Exception("Cannot recover, no clean store."); - } -} - -std::vector<Url> InitialStatusMap::getUrls() const { - std::vector<Url> urls; - for (Map::const_iterator i = map.begin(); i != map.end(); ++i) { - if (i->second) { - std::vector<Url> urls = urlArrayToVector(i->second->getUrls()); - if (!urls.empty()) return urls; - } - } - return std::vector<Url>(); -} - -std::string InitialStatusMap::getFirstConfigStr() const { - assert(!firstConfig.empty()); - return encodeMemberSet(firstConfig); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/InitialStatusMap.h b/cpp/src/qpid/cluster/InitialStatusMap.h deleted file mode 100644 index afa0110836..0000000000 --- a/cpp/src/qpid/cluster/InitialStatusMap.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef QPID_CLUSTER_INITIALSTATUSMAP_H -#define QPID_CLUSTER_INITIALSTATUSMAP_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 "MemberSet.h" -#include "qpid/Url.h" -#include <qpid/framing/ClusterInitialStatusBody.h> -#include <boost/optional.hpp> -#include <vector> - -namespace qpid { -namespace cluster { - -/** - * Track status of cluster members during initialization. - * - * When a new member joins the CPG cluster, all members send an initial-status - * control. This map tracks those controls and provides data to make descisions - * about joining the cluster. - * - */ -class InitialStatusMap -{ - public: - typedef framing::ClusterInitialStatusBody Status; - - InitialStatusMap(const MemberId& self, size_t size); - /** Process a config change. May make isResendNeeded() true. */ - void configChange(const MemberSet& newConfig); - /** @return true if we need to re-send status */ - bool isResendNeeded(); - - /** Process received status */ - void received(const MemberId&, const Status& is); - - /**@return true if the map has an entry for all current cluster members. */ - bool isComplete() const; - - size_t getActualSize() const { return map.size(); } - size_t getRequiredSize() const { return size; } - - /**@return true if the map was completed by the last config change or received. */ - bool transitionToComplete(); - /**@pre isComplete(). @return this node's elders */ - MemberSet getElders() const; - /**@pre isComplete(). @return True if there are active members of the cluster. */ - bool isActive(); - /**@pre isComplete(). @return True if we need to request an update. */ - bool isUpdateNeeded(); - /**@pre isComplete(). @return Cluster-wide cluster ID. */ - framing::Uuid getClusterId(); - /**@pre isComplete(). @throw Exception if there are any inconsistencies. */ - void checkConsistent(); - /*@return cluster URLs */ - std::vector<Url> getUrls() const; - - /** Get first config-change for this member, encoded as a string. - *@pre configChange has been called at least once. - */ - std::string getFirstConfigStr() const; - private: - typedef std::map<MemberId, boost::optional<Status> > Map; - static bool notInitialized(const Map::value_type&); - static bool isActiveEntry(const Map::value_type&); - static bool hasStore(const Map::value_type&); - - Map map; - MemberSet firstConfig; - MemberId self; - bool completed, resendNeeded; - size_t size; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_INITIALSTATUSMAP_H*/ diff --git a/cpp/src/qpid/cluster/LockedConnectionMap.h b/cpp/src/qpid/cluster/LockedConnectionMap.h deleted file mode 100644 index ac744d4f94..0000000000 --- a/cpp/src/qpid/cluster/LockedConnectionMap.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef QPID_CLUSTER_LOCKEDCONNECTIONMAP_H -#define QPID_CLUSTER_LOCKEDCONNECTIONMAP_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/cluster/types.h" -#include "qpid/sys/Mutex.h" -#include "qpid/cluster/Connection.h" - -namespace qpid { -namespace cluster { - -/** - * Thread safe map of connections. - */ -class LockedConnectionMap -{ - public: - void insert(const ConnectionPtr& c) { - sys::Mutex::ScopedLock l(lock); - assert(map.find(c->getId()) == map.end()); - map[c->getId()] = c; - } - - ConnectionPtr getErase(const ConnectionId& c) { - sys::Mutex::ScopedLock l(lock); - Map::iterator i = map.find(c); - if (i != map.end()) { - ConnectionPtr cp = i->second; - map.erase(i); - return cp; - } - else - return 0; - } - - void clear() { sys::Mutex::ScopedLock l(lock); map.clear(); } - - private: - typedef std::map<ConnectionId, ConnectionPtr> Map; - mutable sys::Mutex lock; - Map map; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_LOCKEDCONNECTIONMAP_H*/ diff --git a/cpp/src/qpid/cluster/McastFrameHandler.h b/cpp/src/qpid/cluster/McastFrameHandler.h deleted file mode 100644 index 17e4c2e9f0..0000000000 --- a/cpp/src/qpid/cluster/McastFrameHandler.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef QPID_CLUSTER_MCASTFRAMEHANDLER_H -#define QPID_CLUSTER_MCASTFRAMEHANDLER_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/cluster/types.h" -#include "qpid/cluster/Multicaster.h" -#include "qpid/framing/FrameHandler.h" - -namespace qpid { -namespace cluster { - -/** - * A frame handler that multicasts frames as CONTROL events. - */ -class McastFrameHandler : public framing::FrameHandler -{ - public: - McastFrameHandler(Multicaster& m, const ConnectionId& cid) : mcast(m), connection(cid) {} - void handle(framing::AMQFrame& frame) { mcast.mcastControl(frame, connection); } - private: - Multicaster& mcast; - ConnectionId connection; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_MCASTFRAMEHANDLER_H*/ diff --git a/cpp/src/qpid/cluster/MemberSet.cpp b/cpp/src/qpid/cluster/MemberSet.cpp deleted file mode 100644 index 97748947b3..0000000000 --- a/cpp/src/qpid/cluster/MemberSet.cpp +++ /dev/null @@ -1,60 +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 "MemberSet.h" -#include <ostream> -#include <iterator> -#include <algorithm> - -namespace qpid { -namespace cluster { - -std::string encodeMemberSet(const MemberSet& m) { - std::string addresses; - for (MemberSet::const_iterator i = m.begin(); i != m.end(); ++i) - addresses.append(i->str()); - return addresses; -} - -MemberSet decodeMemberSet(const std::string& s) { - MemberSet set; - for (std::string::const_iterator i = s.begin(); i < s.end(); i += 8) { - assert(size_t(i-s.begin())+8 <= s.size()); - set.insert(MemberId(std::string(i, i+8))); - } - return set; -} - -MemberSet intersection(const MemberSet& a, const MemberSet& b) -{ - MemberSet intersection; - std::set_intersection(a.begin(), a.end(), - b.begin(), b.end(), - std::inserter(intersection, intersection.begin())); - return intersection; - -} - -std::ostream& operator<<(std::ostream& o, const MemberSet& ms) { - copy(ms.begin(), ms.end(), std::ostream_iterator<MemberId>(o, " ")); - return o; -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/MemberSet.h b/cpp/src/qpid/cluster/MemberSet.h deleted file mode 100644 index 7c97145dc1..0000000000 --- a/cpp/src/qpid/cluster/MemberSet.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef QPID_CLUSTER_MEMBERSET_H -#define QPID_CLUSTER_MEMBERSET_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 "types.h" -#include <set> -#include <iosfwd> - -namespace qpid { -namespace cluster { - -typedef std::set<MemberId> MemberSet; - -std::string encodeMemberSet(const MemberSet&); - -MemberSet decodeMemberSet(const std::string&); - -MemberSet intersection(const MemberSet& a, const MemberSet& b); - -std::ostream& operator<<(std::ostream& o, const MemberSet& ms); - - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_MEMBERSET_H*/ diff --git a/cpp/src/qpid/cluster/Multicaster.cpp b/cpp/src/qpid/cluster/Multicaster.cpp deleted file mode 100644 index 217641841c..0000000000 --- a/cpp/src/qpid/cluster/Multicaster.cpp +++ /dev/null @@ -1,105 +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/cluster/Multicaster.h" -#include "qpid/cluster/Cpg.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/log/Statement.h" -#include "qpid/framing/AMQBody.h" -#include "qpid/framing/AMQFrame.h" - -namespace qpid { -namespace cluster { - -Multicaster::Multicaster(Cpg& cpg_, - const boost::shared_ptr<sys::Poller>& poller, - boost::function<void()> onError_) : - onError(onError_), cpg(cpg_), - queue(boost::bind(&Multicaster::sendMcast, this, _1), poller), - ready(false), bypass(true) -{} - -void Multicaster::mcastControl(const framing::AMQBody& body, const ConnectionId& id) { - mcast(Event::control(body, id)); -} - -void Multicaster::mcastControl(const framing::AMQFrame& frame, const ConnectionId& id) { - mcast(Event::control(frame, id)); -} - -void Multicaster::mcastBuffer(const char* data, size_t size, const ConnectionId& id) { - Event e(DATA, id, size); - memcpy(e.getData(), data, size); - mcast(e); -} - -void Multicaster::mcast(const Event& e) { - { - sys::Mutex::ScopedLock l(lock); - if (!ready && e.isConnection()) { - holdingQueue.push_back(e); - return; - } - } - QPID_LOG_IF(trace, e.isControl() && Cluster::loggable(e.getFrame()), "MCAST " << e); - if (bypass) { // direct, don't queue - iovec iov = e.toIovec(); - while (!cpg.mcast(&iov, 1)) - ; - } - else - queue.push(e); -} - -Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(const PollableEventQueue::Batch& values) { - try { - PollableEventQueue::Batch::const_iterator i = values.begin(); - while( i != values.end()) { - iovec iov = i->toIovec(); - if (!cpg.mcast(&iov, 1)) { - // cpg didn't send because of CPG flow control. - break; - } - ++i; - } - return i; - } - catch (const std::exception& e) { - QPID_LOG(critical, "Multicast error: " << e.what()); - queue.stop(); - onError(); - return values.end(); - } -} - -void Multicaster::start() { - queue.start(); - bypass = false; -} - -void Multicaster::setReady() { - sys::Mutex::ScopedLock l(lock); - ready = true; - std::for_each(holdingQueue.begin(), holdingQueue.end(), boost::bind(&Multicaster::mcast, this, _1)); - holdingQueue.clear(); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/Multicaster.h b/cpp/src/qpid/cluster/Multicaster.h deleted file mode 100644 index f70bd5ca31..0000000000 --- a/cpp/src/qpid/cluster/Multicaster.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef QPID_CLUSTER_MULTICASTER_H -#define QPID_CLUSTER_MULTICASTER_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/cluster/types.h" -#include "qpid/cluster/Event.h" -#include "qpid/sys/PollableQueue.h" -#include "qpid/sys/Mutex.h" -#include <boost/shared_ptr.hpp> -#include <deque> - -namespace qpid { - -namespace sys { -class Poller; -} - -namespace cluster { - -class Cpg; - -/** - * Multicast to the cluster. Shared, thread safe object. - * - * holding mode: Hold connection events for later multicast. Cluster - * events are never held. Used during PRE_INIT/INIT state when we - * want to hold any connection traffic till we are read in the - * cluster. - * - * bypass mode: Multicast cluster events directly in the calling - * thread. This mode is used by cluster in PRE_INIT state the poller - * is not yet be active. - * - * Multicaster is created in bypass+holding mode, they are disabled by - * start and setReady respectively. - */ -class Multicaster -{ - public: - /** Starts in initializing mode. */ - Multicaster(Cpg& cpg_, - const boost::shared_ptr<sys::Poller>&, - boost::function<void()> onError - ); - void mcastControl(const framing::AMQBody& controlBody, const ConnectionId&); - void mcastControl(const framing::AMQFrame& controlFrame, const ConnectionId&); - void mcastBuffer(const char*, size_t, const ConnectionId&); - void mcast(const Event& e); - - /** Start the pollable queue, turn off bypass mode. */ - void start(); - /** Switch to ready mode, release held messages. */ - void setReady(); - - private: - typedef sys::PollableQueue<Event> PollableEventQueue; - typedef std::deque<Event> PlainEventQueue; - - PollableEventQueue::Batch::const_iterator sendMcast(const PollableEventQueue::Batch& ); - - sys::Mutex lock; - boost::function<void()> onError; - Cpg& cpg; - PollableEventQueue queue; - bool ready; - PlainEventQueue holdingQueue; - std::vector<struct ::iovec> ioVector; - bool bypass; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_MULTICASTER_H*/ diff --git a/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h b/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h deleted file mode 100644 index 566a82476e..0000000000 --- a/cpp/src/qpid/cluster/NoOpConnectionOutputHandler.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H -#define QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_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/ConnectionOutputHandler.h> - -namespace qpid { - -namespace framing { class AMQFrame; } - -namespace cluster { - -/** - * Output handler shadow connections, simply discards frames. - */ -class NoOpConnectionOutputHandler : public sys::ConnectionOutputHandler -{ - public: - virtual void send(framing::AMQFrame&) {} - virtual void close() {} - virtual void abort() {} - virtual void activateOutput() {} - virtual void giveReadCredit(int32_t) {} -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_NOOPCONNECTIONOUTPUTHANDLER_H*/ diff --git a/cpp/src/qpid/cluster/Numbering.h b/cpp/src/qpid/cluster/Numbering.h deleted file mode 100644 index 99e152c212..0000000000 --- a/cpp/src/qpid/cluster/Numbering.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef QPID_CLUSTER_NUMBERING_H -#define QPID_CLUSTER_NUMBERING_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 <map> -#include <vector> - -namespace qpid { -namespace cluster { - -/** - * A set of numbered T, with two way mapping number->T T->number - * Used to construct numberings of objects by code sending and receiving updates. - */ -template <class T> class Numbering -{ - public: - size_t size() const { return byNumber.size(); } - - size_t add(const T& t) { - size_t n = (*this)[t]; // Already in the set? - if (n == size()) { - byObject[t] = n; - byNumber.push_back(t); - } - return n; - } - - void clear() { byObject.clear(); byNumber.clear(); } - - /**@return object at index n or T() if n > size() */ - T operator[](size_t n) const { return(n < size()) ? byNumber[n] : T(); } - - /**@return index of t or size() if t is not in the map */ - size_t operator[](const T& t) const { - typename Map::const_iterator i = byObject.find(t); - return (i != byObject.end()) ? i->second : size(); - } - - private: - typedef std::map<T, size_t> Map; - Map byObject; - std::vector<T> byNumber; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_NUMBERING_H*/ diff --git a/cpp/src/qpid/cluster/OutputInterceptor.cpp b/cpp/src/qpid/cluster/OutputInterceptor.cpp deleted file mode 100644 index 2cd1cf9a83..0000000000 --- a/cpp/src/qpid/cluster/OutputInterceptor.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 "qpid/cluster/OutputInterceptor.h" -#include "qpid/cluster/Connection.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/framing/ClusterConnectionDeliverDoOutputBody.h" -#include "qpid/framing/AMQFrame.h" -#include "qpid/log/Statement.h" -#include <boost/current_function.hpp> - - -namespace qpid { -namespace cluster { - -using namespace framing; -using namespace std; - -NoOpConnectionOutputHandler OutputInterceptor::discardHandler; - -OutputInterceptor::OutputInterceptor(Connection& p, sys::ConnectionOutputHandler& h) - : parent(p), closing(false), next(&h), sendMax(2048), sent(0), sentDoOutput(false) -{} - -void OutputInterceptor::send(framing::AMQFrame& f) { - sys::Mutex::ScopedLock l(lock); - next->send(f); -} - -void OutputInterceptor::activateOutput() { - sys::Mutex::ScopedLock l(lock); - if (parent.isCatchUp()) - next->activateOutput(); - else - sendDoOutput(sendMax, l); -} - -void OutputInterceptor::abort() { - sys::Mutex::ScopedLock l(lock); - if (parent.isLocal()) { - next->abort(); - } -} - -void OutputInterceptor::giveReadCredit(int32_t credit) { - sys::Mutex::ScopedLock l(lock); - next->giveReadCredit(credit); -} - -// Called in write thread when the IO layer has no more data to write. -// We only process IO callbacks in the write thread during catch-up. -// Normally we run doOutput only on delivery of doOutput requests. -bool OutputInterceptor::doOutput() { - parent.doCatchupIoCallbacks(); - return false; -} - -// Send output up to limit, calculate new limit. -void OutputInterceptor::deliverDoOutput(uint32_t limit) { - sys::Mutex::ScopedLock l(lock); - sentDoOutput = false; - sendMax = limit; - size_t newLimit = limit; - if (parent.isLocal()) { - size_t buffered = next->getBuffered(); - if (buffered == 0 && sent == sendMax) // Could have sent more, increase the limit. - newLimit = sendMax*2; - else if (buffered > 0 && sent > 1) // Data left unsent, reduce the limit. - newLimit = (sendMax + sent) / 2; - } - sent = 0; - while (sent < limit) { - { - sys::Mutex::ScopedUnlock u(lock); - if (!parent.getBrokerConnection()->doOutput()) break; - } - ++sent; - } - if (sent == limit) sendDoOutput(newLimit, l); -} - -void OutputInterceptor::sendDoOutput(size_t newLimit, const sys::Mutex::ScopedLock&) { - if (parent.isLocal() && !sentDoOutput && !closing && parent.isAnnounced()) { - sentDoOutput = true; - parent.getCluster().getMulticast().mcastControl( - ClusterConnectionDeliverDoOutputBody(ProtocolVersion(), newLimit), - parent.getId()); - } -} - -// Called in connection thread when local connection closes. -void OutputInterceptor::closeOutput() { - sys::Mutex::ScopedLock l(lock); - closing = true; - next = &discardHandler; -} - -void OutputInterceptor::close() { - sys::Mutex::ScopedLock l(lock); - next->close(); -} - -size_t OutputInterceptor::getBuffered() const { - sys::Mutex::ScopedLock l(lock); - return next->getBuffered(); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/OutputInterceptor.h b/cpp/src/qpid/cluster/OutputInterceptor.h deleted file mode 100644 index 3abf5273a0..0000000000 --- a/cpp/src/qpid/cluster/OutputInterceptor.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef QPID_CLUSTER_OUTPUTINTERCEPTOR_H -#define QPID_CLUSTER_OUTPUTINTERCEPTOR_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/cluster/NoOpConnectionOutputHandler.h" -#include "qpid/sys/ConnectionOutputHandler.h" -#include "qpid/sys/Mutex.h" -#include "qpid/broker/ConnectionFactory.h" -#include <boost/function.hpp> - -namespace qpid { -namespace framing { class AMQFrame; } -namespace cluster { - -class Connection; - -/** - * Interceptor for connection OutputHandler, manages outgoing message replication. - */ -class OutputInterceptor : public sys::ConnectionOutputHandler { - public: - OutputInterceptor(cluster::Connection& p, sys::ConnectionOutputHandler& h); - - // sys::ConnectionOutputHandler functions - void send(framing::AMQFrame& f); - void abort(); - void activateOutput(); - void giveReadCredit(int32_t); - void close(); - size_t getBuffered() const; - - // Delivery point for doOutput requests. - void deliverDoOutput(uint32_t limit); - // Intercept doOutput requests on Connection. - bool doOutput(); - - void closeOutput(); - - uint32_t getSendMax() const { return sendMax; } - void setSendMax(uint32_t sendMax_) { sendMax=sendMax_; } - - cluster::Connection& parent; - - private: - typedef sys::Mutex::ScopedLock Locker; - - void sendDoOutput(size_t newLimit, const sys::Mutex::ScopedLock&); - - mutable sys::Mutex lock; - bool closing; - sys::ConnectionOutputHandler* next; - static NoOpConnectionOutputHandler discardHandler; - uint32_t sendMax, sent; - bool sentDoOutput; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_OUTPUTINTERCEPTOR_H*/ diff --git a/cpp/src/qpid/cluster/PollableQueue.h b/cpp/src/qpid/cluster/PollableQueue.h deleted file mode 100644 index 10e2ed6ac3..0000000000 --- a/cpp/src/qpid/cluster/PollableQueue.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef QPID_CLUSTER_POLLABLEQUEUE_H -#define QPID_CLUSTER_POLLABLEQUEUE_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/PollableQueue.h" -#include <qpid/log/Statement.h> - -namespace qpid { -namespace cluster { - -/** - * More convenient version of PollableQueue that handles iterating - * over the batch and error handling. - * - * Constructed in "bypass" mode where items are processed directly - * rather than put on the queue. This is important for the - * PRE_INIT stage when Cluster is pumping CPG dispatch directly - * before the poller has started. - * - * Calling start() starts the pollable queue and disabled bypass mode. - */ -template <class T> class PollableQueue : public sys::PollableQueue<T> { - public: - typedef boost::function<void (const T&)> Callback; - typedef boost::function<void()> ErrorCallback; - - PollableQueue(Callback f, ErrorCallback err, const std::string& msg, - const boost::shared_ptr<sys::Poller>& poller) - : sys::PollableQueue<T>(boost::bind(&PollableQueue<T>::handleBatch, this, _1), - poller), - callback(f), error(err), message(msg), bypass(true) - {} - - typename sys::PollableQueue<T>::Batch::const_iterator - handleBatch(const typename sys::PollableQueue<T>::Batch& values) { - try { - typename sys::PollableQueue<T>::Batch::const_iterator i = values.begin(); - while (i != values.end() && !this->isStopped()) { - callback(*i); - ++i; - } - return i; - } - catch (const std::exception& e) { - QPID_LOG(critical, message << ": " << e.what()); - this->stop(); - error(); - return values.end(); - } - } - - void push(const T& t) { - if (bypass) callback(t); - else sys::PollableQueue<T>::push(t); - } - - void bypassOff() { bypass = false; } - - private: - Callback callback; - ErrorCallback error; - std::string message; - bool bypass; -}; - - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_POLLABLEQUEUE_H*/ diff --git a/cpp/src/qpid/cluster/PollerDispatch.cpp b/cpp/src/qpid/cluster/PollerDispatch.cpp deleted file mode 100644 index b8d94b95a5..0000000000 --- a/cpp/src/qpid/cluster/PollerDispatch.cpp +++ /dev/null @@ -1,69 +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/cluster/PollerDispatch.h" - -#include "qpid/log/Statement.h" -#include <boost/bind.hpp> - -namespace qpid { -namespace cluster { - -PollerDispatch::PollerDispatch(Cpg& c, boost::shared_ptr<sys::Poller> p, - boost::function<void()> e) - : cpg(c), poller(p), onError(e), - dispatchHandle(cpg, - boost::bind(&PollerDispatch::dispatch, this, _1), // read - 0, // write - boost::bind(&PollerDispatch::disconnect, this, _1) // disconnect - ), - started(false) -{} - -PollerDispatch::~PollerDispatch() { - if (started) - dispatchHandle.stopWatch(); -} - -void PollerDispatch::start() { - dispatchHandle.startWatch(poller); - started = true; -} - -// Entry point: called by IO to dispatch CPG events. -void PollerDispatch::dispatch(sys::DispatchHandle& h) { - try { - cpg.dispatchAll(); - h.rewatch(); - } catch (const std::exception& e) { - QPID_LOG(critical, "Error in cluster dispatch: " << e.what()); - onError(); - } -} - -// Entry point: called if disconnected from CPG. -void PollerDispatch::disconnect(sys::DispatchHandle& ) { - if (!poller->hasShutdown()) { - QPID_LOG(critical, "Disconnected from cluster"); - onError(); - } -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/PollerDispatch.h b/cpp/src/qpid/cluster/PollerDispatch.h deleted file mode 100644 index 63801e0de9..0000000000 --- a/cpp/src/qpid/cluster/PollerDispatch.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef QPID_CLUSTER_POLLERDISPATCH_H -#define QPID_CLUSTER_POLLERDISPATCH_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/cluster/Cpg.h" -#include "qpid/sys/Poller.h" -#include "qpid/sys/DispatchHandle.h" -#include <boost/function.hpp> - -namespace qpid { -namespace cluster { - -/** - * Dispatch CPG events via the poller. - */ -class PollerDispatch { - public: - PollerDispatch(Cpg&, boost::shared_ptr<sys::Poller> poller, - boost::function<void()> onError) ; - - ~PollerDispatch(); - - void start(); - - private: - // Poller callbacks - void dispatch(sys::DispatchHandle&); // Dispatch CPG events. - void disconnect(sys::DispatchHandle&); // CPG was disconnected - - Cpg& cpg; - boost::shared_ptr<sys::Poller> poller; - boost::function<void()> onError; - sys::DispatchHandleRef dispatchHandle; - bool started; - - -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_POLLERDISPATCH_H*/ diff --git a/cpp/src/qpid/cluster/ProxyInputHandler.h b/cpp/src/qpid/cluster/ProxyInputHandler.h deleted file mode 100644 index ad7f2c44bd..0000000000 --- a/cpp/src/qpid/cluster/ProxyInputHandler.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef QPID_CLUSTER_PROXYINPUTHANDLER_H -#define QPID_CLUSTER_PROXYINPUTHANDLER_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/ConnectionInputHandler.h" -#include <boost/intrusive_ptr.hpp> - -namespace qpid { - -namespace framing { class AMQFrame; } - -namespace cluster { - -/** - * Proxies ConnectionInputHandler functions and ensures target.closed() - * is called, on deletion if not before. - */ -class ProxyInputHandler : public sys::ConnectionInputHandler -{ - public: - ProxyInputHandler(boost::intrusive_ptr<cluster::Connection> t) : target(t) {} - ~ProxyInputHandler() { closed(); } - - void received(framing::AMQFrame& f) { target->received(f); } - void closed() { if (target) target->closed(); target = 0; } - void idleOut() { target->idleOut(); } - void idleIn() { target->idleIn(); } - bool doOutput() { return target->doOutput(); } - - private: - boost::intrusive_ptr<cluster::Connection> target; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_PROXYINPUTHANDLER_H*/ diff --git a/cpp/src/qpid/cluster/Quorum.h b/cpp/src/qpid/cluster/Quorum.h deleted file mode 100644 index bbfa473f94..0000000000 --- a/cpp/src/qpid/cluster/Quorum.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef QPID_CLUSTER_QUORUM_H -#define QPID_CLUSTER_QUORUM_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 "config.h" - -#if HAVE_LIBCMAN_H -#include "qpid/cluster/Quorum_cman.h" -#else -#include "qpid/cluster/Quorum_null.h" -#endif - -#endif /*!QPID_CLUSTER_QUORUM_H*/ diff --git a/cpp/src/qpid/cluster/Quorum_cman.cpp b/cpp/src/qpid/cluster/Quorum_cman.cpp deleted file mode 100644 index 728f824b16..0000000000 --- a/cpp/src/qpid/cluster/Quorum_cman.cpp +++ /dev/null @@ -1,105 +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/cluster/Quorum_cman.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/log/Statement.h" -#include "qpid/Options.h" -#include "qpid/sys/Time.h" -#include "qpid/sys/posix/PrivatePosix.h" - -namespace qpid { -namespace cluster { - -namespace { - -boost::function<void()> errorFn; - -void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int /*arg*/) { - if (reason == CMAN_REASON_STATECHANGE && !cman_is_quorate(handle)) { - QPID_LOG(critical, "Lost contact with cluster quorum."); - if (errorFn) errorFn(); - cman_stop_notification(handle); - } -} -} - -Quorum::Quorum(boost::function<void()> err) : cman(0), cmanFd(0) { - errorFn = err; -} - -Quorum::~Quorum() { - if (dispatchHandle.get()) dispatchHandle->stopWatch(); - dispatchHandle.reset(); - if (cman) cman_finish(cman); -} - -void Quorum::start(boost::shared_ptr<sys::Poller> p) { - poller = p; - QPID_LOG(debug, "Connecting to quorum service."); - cman = cman_init(0); - if (cman == 0) throw ErrnoException("Can't connect to cman service"); - if (!cman_is_quorate(cman)) { - QPID_LOG(notice, "Waiting for cluster quorum."); - while(!cman_is_quorate(cman)) sys::sleep(5); - } - int err = cman_start_notification(cman, cmanCallbackFn); - if (err != 0) throw ErrnoException("Can't register for cman notifications"); - watch(getFd()); -} - -void Quorum::watch(int fd) { - cmanFd = fd; - if (dispatchHandle.get()) dispatchHandle->stopWatch(); - ioHandle.reset(new sys::PosixIOHandle(cmanFd)); - dispatchHandle.reset( - new sys::DispatchHandleRef( - *ioHandle, // This must outlive the dispatchHandleRef - boost::bind(&Quorum::dispatch, this, _1), // read - 0, // write - boost::bind(&Quorum::disconnect, this, _1) // disconnect - )); - dispatchHandle->startWatch(poller); -} - -int Quorum::getFd() { - int fd = cman_get_fd(cman); - if (fd == 0) throw ErrnoException("Can't get cman file descriptor"); - return fd; -} - -void Quorum::dispatch(sys::DispatchHandle&) { - try { - cman_dispatch(cman, CMAN_DISPATCH_ALL); - int fd = getFd(); - if (fd != cmanFd) watch(fd); - } catch (const std::exception& e) { - QPID_LOG(critical, "Error in quorum dispatch: " << e.what()); - errorFn(); - } -} - -void Quorum::disconnect(sys::DispatchHandle&) { - QPID_LOG(critical, "Disconnected from quorum service"); - errorFn(); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/Quorum_cman.h b/cpp/src/qpid/cluster/Quorum_cman.h deleted file mode 100644 index 98e6baee89..0000000000 --- a/cpp/src/qpid/cluster/Quorum_cman.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef QPID_CLUSTER_QUORUM_CMAN_H -#define QPID_CLUSTER_QUORUM_CMAN_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/DispatchHandle.h> -#include <boost/function.hpp> -#include <boost/shared_ptr.hpp> -#include <memory> - -extern "C" { -#include <libcman.h> -} - -namespace qpid { -namespace sys { -class Poller; -class PosixIOHandle; -} - -namespace cluster { -class Cluster; - -class Quorum { - public: - Quorum(boost::function<void ()> onError); - ~Quorum(); - void start(boost::shared_ptr<sys::Poller>); - - private: - void dispatch(sys::DispatchHandle&); - void disconnect(sys::DispatchHandle&); - int getFd(); - void watch(int fd); - - cman_handle_t cman; - int cmanFd; - std::auto_ptr<sys::PosixIOHandle> ioHandle; - std::auto_ptr<sys::DispatchHandleRef> dispatchHandle; - boost::shared_ptr<sys::Poller> poller; -}; - - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_QUORUM_CMAN_H*/ diff --git a/cpp/src/qpid/cluster/RetractClient.cpp b/cpp/src/qpid/cluster/RetractClient.cpp deleted file mode 100644 index a8c4b0d543..0000000000 --- a/cpp/src/qpid/cluster/RetractClient.cpp +++ /dev/null @@ -1,62 +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/cluster/RetractClient.h" -#include "qpid/cluster/UpdateClient.h" -#include "qpid/framing/ClusterConnectionRetractOfferBody.h" -#include "qpid/client/ConnectionAccess.h" -#include "qpid/client/ConnectionImpl.h" -#include "qpid/log/Statement.h" - -namespace qpid { -namespace cluster { - -using namespace framing; - -namespace { - -struct AutoClose { - client::Connection& connection; - AutoClose(client::Connection& c) : connection(c) {} - ~AutoClose() { connection.close(); } -}; -} - -RetractClient::RetractClient(const Url& u, const client::ConnectionSettings& cs) - : url(u), connectionSettings(cs) -{} - -RetractClient::~RetractClient() { delete this; } - - -void RetractClient::run() { - try { - client::Connection c = UpdateClient::catchUpConnection(); - c.open(url, connectionSettings); - AutoClose ac(c); - AMQFrame retract((ClusterConnectionRetractOfferBody())); - client::ConnectionAccess::getImpl(c)->expand(retract.encodedSize(), false); - client::ConnectionAccess::getImpl(c)->handle(retract); - } catch (const std::exception& e) { - QPID_LOG(error, " while retracting retract to " << url << ": " << e.what()); - } -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/RetractClient.h b/cpp/src/qpid/cluster/RetractClient.h deleted file mode 100644 index 533fc3f7ef..0000000000 --- a/cpp/src/qpid/cluster/RetractClient.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef QPID_CLUSTER_RETRACTCLIENT_H -#define QPID_CLUSTER_RETRACTCLIENT_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/client/ConnectionSettings.h" -#include "qpid/sys/Runnable.h" -#include "qpid/Url.h" - - -namespace qpid { -namespace cluster { - -/** - * A client that retracts an offer to a remote broker using AMQP. @see UpdateClient - */ -class RetractClient : public sys::Runnable { - public: - - RetractClient(const Url&, const client::ConnectionSettings&); - ~RetractClient(); - void run(); // Will delete this when finished. - - private: - Url url; - client::ConnectionSettings connectionSettings; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_RETRACTCLIENT_H*/ diff --git a/cpp/src/qpid/cluster/SecureConnectionFactory.cpp b/cpp/src/qpid/cluster/SecureConnectionFactory.cpp deleted file mode 100644 index 2672d8360c..0000000000 --- a/cpp/src/qpid/cluster/SecureConnectionFactory.cpp +++ /dev/null @@ -1,73 +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/cluster/SecureConnectionFactory.h" -#include "qpid/framing/ProtocolVersion.h" -#include "qpid/cluster/ConnectionCodec.h" -#include "qpid/broker/SecureConnection.h" -#include "qpid/sys/SecuritySettings.h" -#include "qpid/log/Statement.h" -#include <memory> - - -namespace qpid { -namespace cluster { - -using framing::ProtocolVersion; -using qpid::sys::SecuritySettings; -using qpid::broker::SecureConnection; - -typedef std::auto_ptr<qpid::broker::SecureConnection> SecureConnectionPtr; -typedef std::auto_ptr<qpid::sys::ConnectionCodec> CodecPtr; - -SecureConnectionFactory::SecureConnectionFactory(CodecFactoryPtr f) : codecFactory(f) { -} - -sys::ConnectionCodec* -SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, - const SecuritySettings& external) { - CodecPtr codec(codecFactory->create(v, out, id, external)); - ConnectionCodec* clusterCodec = dynamic_cast<qpid::cluster::ConnectionCodec*>(codec.get()); - if (clusterCodec) { - SecureConnectionPtr sc(new SecureConnection()); - clusterCodec->setSecureConnection(sc.get()); - sc->setCodec(codec); - return sc.release(); - } - return 0; -} - -sys::ConnectionCodec* -SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id, - const SecuritySettings& external) { - // used to create connections from one broker to another - CodecPtr codec(codecFactory->create(out, id, external)); - ConnectionCodec* clusterCodec = dynamic_cast<qpid::cluster::ConnectionCodec*>(codec.get()); - if (clusterCodec) { - SecureConnectionPtr sc(new SecureConnection()); - clusterCodec->setSecureConnection(sc.get()); - sc->setCodec(codec); - return sc.release(); - } - return 0; -} - - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/StoreStatus.cpp b/cpp/src/qpid/cluster/StoreStatus.cpp deleted file mode 100644 index 14c999bb05..0000000000 --- a/cpp/src/qpid/cluster/StoreStatus.cpp +++ /dev/null @@ -1,170 +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 "StoreStatus.h" -#include "qpid/Exception.h" -#include "qpid/Msg.h" -#include "qpid/log/Statement.h" -#include <boost/filesystem/path.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/filesystem/operations.hpp> -#include <boost/scoped_array.hpp> -#include <fstream> -#include <sstream> - -namespace qpid { -namespace cluster { - -using framing::Uuid; -using namespace framing::cluster; -namespace fs=boost::filesystem; -using namespace std; - -StoreStatus::StoreStatus(const std::string& d) - : state(STORE_STATE_NO_STORE), dataDir(d) -{} - -namespace { - -const char* SUBDIR="cluster"; -const char* STORE_STATUS="store.status"; - -string readFile(const fs::path& path) { - fs::ifstream is; - is.exceptions(std::ios::badbit | std::ios::failbit); - is.open(path); - // get length of file: - is.seekg (0, ios::end); - size_t length = is.tellg(); - is.seekg (0, ios::beg); - // load data - boost::scoped_array<char> buffer(new char[length]); - is.read(buffer.get(), length); - is.close(); - return string(buffer.get(), length); -} - -void writeFile(const fs::path& path, const string& data) { - fs::ofstream os; - os.exceptions(std::ios::badbit | std::ios::failbit); - os.open(path); - os.write(data.data(), data.size()); - os.close(); -} - -} // namespace - - -void StoreStatus::load() { - if (dataDir.empty()) { - throw Exception(QPID_MSG("No data-dir: When a store is loaded together with clustering, --data-dir must be specified.")); - } - try { - fs::path dir = fs::path(dataDir, fs::native)/SUBDIR; - create_directory(dir); - fs::path file = dir/STORE_STATUS; - if (fs::exists(file)) { - string data = readFile(file); - istringstream is(data); - is.exceptions(std::ios::badbit | std::ios::failbit); - is >> ws >> clusterId >> ws >> shutdownId; - if (!clusterId) - throw Exception(QPID_MSG("Invalid cluster store state, no cluster-id")); - if (shutdownId) state = STORE_STATE_CLEAN_STORE; - else state = STORE_STATE_DIRTY_STORE; - } - else { // Starting from empty store - clusterId = Uuid(true); - save(); - state = STORE_STATE_EMPTY_STORE; - } - } - catch (const std::exception&e) { - throw Exception(QPID_MSG("Cannot load cluster store status: " << e.what())); - } -} - -void StoreStatus::save() { - if (dataDir.empty()) return; - try { - ostringstream os; - os << clusterId << endl << shutdownId << endl; - fs::path file = fs::path(dataDir, fs::native)/SUBDIR/STORE_STATUS; - writeFile(file, os.str()); - } - catch (const std::exception& e) { - throw Exception(QPID_MSG("Cannot save cluster store status: " << e.what())); - } -} - -bool StoreStatus::hasStore() const { - return state != framing::cluster::STORE_STATE_NO_STORE; -} - -void StoreStatus::dirty() { - assert(hasStore()); - if (shutdownId) { - shutdownId = Uuid(); - save(); - } - state = STORE_STATE_DIRTY_STORE; -} - -void StoreStatus::clean(const Uuid& shutdownId_) { - assert(hasStore()); - assert(shutdownId_); - if (shutdownId_ != shutdownId) { - shutdownId = shutdownId_; - save(); - } - state = STORE_STATE_CLEAN_STORE; -} - -void StoreStatus::setClusterId(const Uuid& clusterId_) { - clusterId = clusterId_; - save(); -} - -const char* stateName(StoreState s) { - switch (s) { - case STORE_STATE_NO_STORE: return "none"; - case STORE_STATE_EMPTY_STORE: return "empty"; - case STORE_STATE_DIRTY_STORE: return "dirty"; - case STORE_STATE_CLEAN_STORE: return "clean"; - } - assert(0); - return "unknown"; -} - -ostream& operator<<(ostream& o, framing::cluster::StoreState s) { return o << stateName(s); } - -ostream& operator<<(ostream& o, const StoreStatus& s) { - o << s.getState(); - if (s.getState() == STORE_STATE_DIRTY_STORE) - o << " cluster-id=" << s.getClusterId(); - if (s.getState() == STORE_STATE_CLEAN_STORE) { - o << " cluster-id=" << s.getClusterId() - << " shutdown-id=" << s.getShutdownId(); - } - return o; -} - -}} // namespace qpid::cluster - diff --git a/cpp/src/qpid/cluster/StoreStatus.h b/cpp/src/qpid/cluster/StoreStatus.h deleted file mode 100644 index 7442fcf02c..0000000000 --- a/cpp/src/qpid/cluster/StoreStatus.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef QPID_CLUSTER_STORESTATE_H -#define QPID_CLUSTER_STORESTATE_H - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/framing/Uuid.h" -#include "qpid/framing/SequenceNumber.h" -#include "qpid/framing/enum.h" -#include <iosfwd> - -namespace qpid { -namespace cluster { - -/** - * State of the store for cluster purposes. - */ -class StoreStatus -{ - public: - typedef framing::Uuid Uuid; - typedef framing::cluster::StoreState StoreState; - - StoreStatus(const std::string& dir); - - framing::cluster::StoreState getState() const { return state; } - - const Uuid& getClusterId() const { return clusterId; } - void setClusterId(const Uuid&); - const Uuid& getShutdownId() const { return shutdownId; } - - void load(); - void dirty(); // Mark the store in use. - void clean(const Uuid& shutdownId); // Mark the store clean. - bool hasStore() const; - - private: - void save(); - - framing::cluster::StoreState state; - Uuid clusterId, shutdownId; - std::string dataDir; -}; - -const char* stateName(framing::cluster::StoreState); -std::ostream& operator<<(std::ostream&, framing::cluster::StoreState); -std::ostream& operator<<(std::ostream&, const StoreStatus&); -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_STORESTATE_H*/ diff --git a/cpp/src/qpid/cluster/UpdateClient.cpp b/cpp/src/qpid/cluster/UpdateClient.cpp deleted file mode 100644 index 8737418570..0000000000 --- a/cpp/src/qpid/cluster/UpdateClient.cpp +++ /dev/null @@ -1,736 +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/amqp_0_10/Codecs.h" -#include "qpid/cluster/UpdateClient.h" -#include "qpid/cluster/Cluster.h" -#include "qpid/cluster/ClusterMap.h" -#include "qpid/cluster/Connection.h" -#include "qpid/cluster/Decoder.h" -#include "qpid/cluster/ExpiryPolicy.h" -#include "qpid/cluster/UpdateDataExchange.h" -#include "qpid/client/SessionBase_0_10Access.h" -#include "qpid/client/ConnectionAccess.h" -#include "qpid/client/SessionImpl.h" -#include "qpid/client/ConnectionImpl.h" -#include "qpid/client/Future.h" -#include "qpid/broker/Broker.h" -#include "qpid/broker/Fairshare.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/QueueRegistry.h" -#include "qpid/broker/LinkRegistry.h" -#include "qpid/broker/Bridge.h" -#include "qpid/broker/Link.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/Exchange.h" -#include "qpid/broker/ExchangeRegistry.h" -#include "qpid/broker/SessionHandler.h" -#include "qpid/broker/SessionState.h" -#include "qpid/broker/TxOpVisitor.h" -#include "qpid/broker/DtxAck.h" -#include "qpid/broker/DtxBuffer.h" -#include "qpid/broker/DtxWorkRecord.h" -#include "qpid/broker/TxAccept.h" -#include "qpid/broker/TxPublish.h" -#include "qpid/broker/RecoveredDequeue.h" -#include "qpid/broker/RecoveredEnqueue.h" -#include "qpid/broker/StatefulQueueObserver.h" -#include "qpid/framing/MessageTransferBody.h" -#include "qpid/framing/ClusterConnectionMembershipBody.h" -#include "qpid/framing/ClusterConnectionShadowReadyBody.h" -#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" -#include "qpid/log/Statement.h" -#include "qpid/types/Variant.h" -#include "qpid/Url.h" -#include "qmf/org/apache/qpid/broker/ManagementSetupState.h" -#include <boost/bind.hpp> -#include <boost/cast.hpp> -#include <algorithm> -#include <iterator> -#include <sstream> - -namespace qpid { -namespace cluster { - -using std::string; - -using amqp_0_10::ListCodec; -using broker::Broker; -using broker::Exchange; -using broker::Queue; -using broker::QueueBinding; -using broker::Message; -using broker::SemanticState; -using types::Variant; - -using namespace framing; -namespace arg=client::arg; -using client::SessionBase_0_10Access; - -namespace _qmf = qmf::org::apache::qpid::broker; - -// Reserved exchange/queue name for catch-up, avoid clashes with user queues/exchanges. -const std::string UpdateClient::UPDATE("x-qpid.cluster-update"); -// Name for header used to carry expiration information. -const std::string UpdateClient::X_QPID_EXPIRATION = "x-qpid.expiration"; -// Headers used to flag headers/properties added by the UpdateClient so they can be -// removed on the other side. -const std::string UpdateClient::X_QPID_NO_MESSAGE_PROPS = "x-qpid.no-message-props"; -const std::string UpdateClient::X_QPID_NO_HEADERS = "x-qpid.no-headers"; - -std::ostream& operator<<(std::ostream& o, const UpdateClient& c) { - return o << "cluster(" << c.updaterId << " UPDATER)"; -} - -struct ClusterConnectionProxy : public AMQP_AllProxy::ClusterConnection, public framing::FrameHandler -{ - boost::shared_ptr<qpid::client::ConnectionImpl> connection; - - ClusterConnectionProxy(client::Connection c) : - AMQP_AllProxy::ClusterConnection(*static_cast<framing::FrameHandler*>(this)), - connection(client::ConnectionAccess::getImpl(c)) {} - ClusterConnectionProxy(client::AsyncSession s) : - AMQP_AllProxy::ClusterConnection(SessionBase_0_10Access(s).get()->out) {} - - void handle(framing::AMQFrame& f) - { - assert(connection); - connection->expand(f.encodedSize(), false); - connection->handle(f); - } -}; - -// Create a connection with special version that marks it as a catch-up connection. -client::Connection UpdateClient::catchUpConnection() { - client::Connection c; - client::ConnectionAccess::setVersion(c, ProtocolVersion(0x80 , 0x80 + 10)); - return c; -} - -// Send a control body directly to the session. -void send(client::AsyncSession& s, const AMQBody& body) { - client::SessionBase_0_10Access sb(s); - sb.get()->send(body); -} - -// TODO aconway 2008-09-24: optimization: update connections/sessions in parallel. - -UpdateClient::UpdateClient(const MemberId& updater, const MemberId& updatee, const Url& url, - broker::Broker& broker, const ClusterMap& m, ExpiryPolicy& expiry_, - const Cluster::ConnectionVector& cons, Decoder& decoder_, - const boost::function<void()>& ok, - const boost::function<void(const std::exception&)>& fail, - const client::ConnectionSettings& cs -) - : updaterId(updater), updateeId(updatee), updateeUrl(url), updaterBroker(broker), map(m), - expiry(expiry_), connections(cons), decoder(decoder_), - connection(catchUpConnection()), shadowConnection(catchUpConnection()), - done(ok), failed(fail), connectionSettings(cs) -{} - -UpdateClient::~UpdateClient() {} - -void UpdateClient::run() { - try { - connection.open(updateeUrl, connectionSettings); - session = connection.newSession(UPDATE); - session.sync(); - update(); - done(); - } catch (const std::exception& e) { - failed(e); - } - delete this; -} - -void UpdateClient::update() { - QPID_LOG(debug, *this << " updating state to " << updateeId - << " at " << updateeUrl); - Broker& b = updaterBroker; - - if(b.getExpiryPolicy()) { - QPID_LOG(debug, *this << "Updating updatee with cluster time"); - qpid::sys::AbsTime clusterTime = b.getExpiryPolicy()->getCurrentTime(); - int64_t time = qpid::sys::Duration(qpid::sys::EPOCH, clusterTime); - ClusterConnectionProxy(session).clock(time); - } - - updateManagementSetupState(); - - b.getExchanges().eachExchange(boost::bind(&UpdateClient::updateExchange, this, _1)); - b.getQueues().eachQueue(boost::bind(&UpdateClient::updateNonExclusiveQueue, this, _1)); - - // Update queue is used to transfer acquired messages that are no - // longer on their original queue. - session.queueDeclare(arg::queue=UPDATE, arg::autoDelete=true); - session.sync(); - - std::for_each(connections.begin(), connections.end(), - boost::bind(&UpdateClient::updateConnection, this, _1)); - - // some Queue Observers need session state & msgs synced first, so sync observers now - b.getQueues().eachQueue(boost::bind(&UpdateClient::updateQueueObservers, this, _1)); - - // Update queue listeners: must come after sessions so consumerNumbering is populated - b.getQueues().eachQueue(boost::bind(&UpdateClient::updateQueueListeners, this, _1)); - - updateLinks(); - updateManagementAgent(); - updateDtxManager(); - session.queueDelete(arg::queue=UPDATE); - - session.close(); - - ClusterConnectionMembershipBody membership; - map.toMethodBody(membership); - AMQFrame frame(membership); - client::ConnectionAccess::getImpl(connection)->expand(frame.encodedSize(), false); - client::ConnectionAccess::getImpl(connection)->handle(frame); - - // NOTE: connection will be closed from the other end, don't close - // it here as that causes a race. - - // TODO aconway 2010-03-15: This sleep avoids the race condition - // described in // https://bugzilla.redhat.com/show_bug.cgi?id=568831. - // It allows the connection to fully close before destroying the - // Connection object. Remove when the bug is fixed. - // - sys::usleep(10*1000); - - QPID_LOG(debug, *this << " update completed to " << updateeId << " at " << updateeUrl); -} - -namespace { -template <class T> std::string encode(const T& t) { - std::string encoded; - encoded.resize(t.encodedSize()); - framing::Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); - t.encode(buf); - return encoded; -} -} // namespace - - -// Propagate the management state -void UpdateClient::updateManagementSetupState() -{ - management::ManagementAgent* agent = updaterBroker.getManagementAgent(); - if (!agent) return; - - QPID_LOG(debug, *this << " updating management setup-state."); - std::string vendor, product, instance; - agent->getName(vendor, product, instance); - ClusterConnectionProxy(session).managementSetupState( - agent->getNextObjectId(), agent->getBootSequence(), agent->getUuid(), - vendor, product, instance); -} - -void UpdateClient::updateManagementAgent() -{ - management::ManagementAgent* agent = updaterBroker.getManagementAgent(); - if (!agent) return; - string data; - - QPID_LOG(debug, *this << " updating management schemas. ") - agent->exportSchemas(data); - session.messageTransfer( - arg::content=client::Message(data, UpdateDataExchange::MANAGEMENT_SCHEMAS_KEY), - arg::destination=UpdateDataExchange::EXCHANGE_NAME); - - QPID_LOG(debug, *this << " updating management agents. ") - agent->exportAgents(data); - session.messageTransfer( - arg::content=client::Message(data, UpdateDataExchange::MANAGEMENT_AGENTS_KEY), - arg::destination=UpdateDataExchange::EXCHANGE_NAME); - - QPID_LOG(debug, *this << " updating management deleted objects. ") - typedef management::ManagementAgent::DeletedObjectList DeletedObjectList; - DeletedObjectList deleted; - agent->exportDeletedObjects(deleted); - Variant::List list; - for (DeletedObjectList::iterator i = deleted.begin(); i != deleted.end(); ++i) { - string encoded; - (*i)->encode(encoded); - list.push_back(encoded); - } - ListCodec::encode(list, data); - session.messageTransfer( - arg::content=client::Message(data, UpdateDataExchange::MANAGEMENT_DELETED_OBJECTS_KEY), - arg::destination=UpdateDataExchange::EXCHANGE_NAME); -} - -void UpdateClient::updateExchange(const boost::shared_ptr<Exchange>& ex) { - QPID_LOG(debug, *this << " updating exchange " << ex->getName()); - ClusterConnectionProxy(session).exchange(encode(*ex)); -} - -/** Bind a queue to the update exchange and update messges to it - * setting the message possition as needed. - */ -class MessageUpdater { - std::string queue; - bool haveLastPos; - framing::SequenceNumber lastPos; - client::AsyncSession session; - ExpiryPolicy& expiry; - - public: - - MessageUpdater(const string& q, const client::AsyncSession s, ExpiryPolicy& expiry_) : queue(q), haveLastPos(false), session(s), expiry(expiry_) { - session.exchangeBind(queue, UpdateClient::UPDATE); - } - - ~MessageUpdater() { - try { - session.exchangeUnbind(queue, UpdateClient::UPDATE); - } - catch (const std::exception& e) { - // Don't throw in a destructor. - QPID_LOG(error, "Unbinding update queue " << queue << ": " << e.what()); - } - } - - void updateQueuedMessage(const broker::QueuedMessage& message) { - // Send the queue position if necessary. - if (!haveLastPos || message.position - lastPos != 1) { - ClusterConnectionProxy(session).queuePosition(queue, message.position.getValue()-1); - haveLastPos = true; - } - lastPos = message.position; - - // if the ttl > 0, we need to send the calculated expiration time to the updatee - const DeliveryProperties* dprops = - message.payload->getProperties<DeliveryProperties>(); - if (dprops && dprops->getTtl() > 0) { - bool hadMessageProps = - message.payload->hasProperties<framing::MessageProperties>(); - const framing::MessageProperties* mprops = - message.payload->getProperties<framing::MessageProperties>(); - bool hadApplicationHeaders = mprops->hasApplicationHeaders(); - message.payload->insertCustomProperty(UpdateClient::X_QPID_EXPIRATION, - sys::Duration(sys::EPOCH, message.payload->getExpiration())); - // If message properties or application headers didn't exist - // prior to us adding data, we want to remove them on the other side. - if (!hadMessageProps) - message.payload->insertCustomProperty(UpdateClient::X_QPID_NO_MESSAGE_PROPS, 0); - else if (!hadApplicationHeaders) - message.payload->insertCustomProperty(UpdateClient::X_QPID_NO_HEADERS, 0); - } - - // We can't send a broker::Message via the normal client API, - // and it would be expensive to copy it into a client::Message - // so we go a bit under the client API covers here. - // - SessionBase_0_10Access sb(session); - // Disable client code that clears the delivery-properties.exchange - sb.get()->setDoClearDeliveryPropertiesExchange(false); - framing::MessageTransferBody transfer( - *message.payload->getFrames().as<framing::MessageTransferBody>()); - transfer.setDestination(UpdateClient::UPDATE); - - sb.get()->send(transfer, message.payload->getFrames(), - !message.payload->isContentReleased()); - if (message.payload->isContentReleased()){ - uint16_t maxFrameSize = sb.get()->getConnection()->getNegotiatedSettings().maxFrameSize; - uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); - bool morecontent = true; - for (uint64_t offset = 0; morecontent; offset += maxContentSize) - { - AMQFrame frame((AMQContentBody())); - morecontent = message.payload->getContentFrame( - *(message.queue), frame, maxContentSize, offset); - sb.get()->sendRawFrame(frame); - } - } - } - - void updateMessage(const boost::intrusive_ptr<broker::Message>& message) { - updateQueuedMessage(broker::QueuedMessage(0, message, haveLastPos? lastPos.getValue()+1 : 1)); - } -}; - -void UpdateClient::updateQueue(client::AsyncSession& s, const boost::shared_ptr<Queue>& q) { - broker::Exchange::shared_ptr alternateExchange = q->getAlternateExchange(); - _qmf::Queue* mgmtQueue = dynamic_cast<_qmf::Queue*>(q->GetManagementObject()); - s.queueDeclare( - arg::queue = q->getName(), - arg::durable = q->isDurable(), - arg::autoDelete = q->isAutoDelete(), - arg::alternateExchange = alternateExchange ? alternateExchange->getName() : "", - arg::arguments = q->getSettings(), - arg::exclusive = mgmtQueue && mgmtQueue->get_exclusive() - ); - MessageUpdater updater(q->getName(), s, expiry); - q->eachMessage(boost::bind(&MessageUpdater::updateQueuedMessage, &updater, _1)); - q->eachBinding(boost::bind(&UpdateClient::updateBinding, this, s, q->getName(), _1)); - ClusterConnectionProxy(s).queuePosition(q->getName(), q->getPosition()); - uint priority, count; - if (qpid::broker::Fairshare::getState(q->getMessages(), priority, count)) { - ClusterConnectionProxy(s).queueFairshareState(q->getName(), priority, count); - } - - ClusterConnectionProxy(s).queueDequeueSincePurgeState(q->getName(), q->getDequeueSincePurge()); -} - -void UpdateClient::updateExclusiveQueue(const boost::shared_ptr<broker::Queue>& q) { - QPID_LOG(debug, *this << " updating exclusive queue " << q->getName() << " on " << shadowSession.getId()); - updateQueue(shadowSession, q); -} - -void UpdateClient::updateNonExclusiveQueue(const boost::shared_ptr<broker::Queue>& q) { - if (!q->hasExclusiveOwner()) { - QPID_LOG(debug, *this << " updating queue " << q->getName()); - updateQueue(session, q); - }//else queue will be updated as part of session state of owning session -} - -void UpdateClient::updateBinding(client::AsyncSession& s, const std::string& queue, const QueueBinding& binding) { - if (binding.exchange.size()) - s.exchangeBind(queue, binding.exchange, binding.key, binding.args); - //else its the default exchange and there is no need to replicate - //the binding, the creation of the queue will have done so - //automatically -} - -void UpdateClient::updateOutputTask(const sys::OutputTask* task) { - const SemanticState::ConsumerImpl* cci = - boost::polymorphic_downcast<const SemanticState::ConsumerImpl*> (task); - SemanticState::ConsumerImpl* ci = const_cast<SemanticState::ConsumerImpl*>(cci); - uint16_t channel = ci->getParent().getSession().getChannel(); - ClusterConnectionProxy(shadowConnection).outputTask(channel, ci->getTag()); - QPID_LOG(debug, *this << " updating output task " << ci->getTag() - << " channel=" << channel); -} - -void UpdateClient::updateConnection(const boost::intrusive_ptr<Connection>& updateConnection) { - QPID_LOG(debug, *this << " updating connection " << *updateConnection); - assert(updateConnection->getBrokerConnection()); - broker::Connection& bc = *updateConnection->getBrokerConnection(); - - // Send the management ID first on the main connection. - std::string mgmtId = updateConnection->getBrokerConnection()->getMgmtId(); - ClusterConnectionProxy(session).shadowPrepare(mgmtId); - // Make sure its received before opening shadow connection - session.sync(); - - // Open shadow connection and update it. - shadowConnection = catchUpConnection(); - - connectionSettings.maxFrameSize = bc.getFrameMax(); - shadowConnection.open(updateeUrl, connectionSettings); - ClusterConnectionProxy(shadowConnection).shadowSetUser(bc.getUserId()); - - bc.eachSessionHandler(boost::bind(&UpdateClient::updateSession, this, _1)); - // Safe to use decoder here because we are stalled for update. - std::pair<const char*, size_t> fragment = decoder.get(updateConnection->getId()).getFragment(); - bc.getOutputTasks().eachOutput( - boost::bind(&UpdateClient::updateOutputTask, this, _1)); - ClusterConnectionProxy(shadowConnection).shadowReady( - updateConnection->getId().getMember(), - updateConnection->getId().getNumber(), - bc.getMgmtId(), - bc.getUserId(), - string(fragment.first, fragment.second), - updateConnection->getOutput().getSendMax() - ); - shadowConnection.close(); - QPID_LOG(debug, *this << " updated connection " << *updateConnection); -} - -void UpdateClient::updateSession(broker::SessionHandler& sh) { - broker::SessionState* ss = sh.getSession(); - if (!ss) return; // no session. - - QPID_LOG(debug, *this << " updating session " << ss->getId()); - - // Create a client session to update session state. - boost::shared_ptr<client::ConnectionImpl> cimpl = client::ConnectionAccess::getImpl(shadowConnection); - boost::shared_ptr<client::SessionImpl> simpl = cimpl->newSession(ss->getId().getName(), ss->getTimeout(), sh.getChannel()); - simpl->disableAutoDetach(); - client::SessionBase_0_10Access(shadowSession).set(simpl); - AMQP_AllProxy::ClusterConnection proxy(simpl->out); - - // Re-create session state on remote connection. - - QPID_LOG(debug, *this << " updating exclusive queues."); - ss->getSessionAdapter().eachExclusiveQueue(boost::bind(&UpdateClient::updateExclusiveQueue, this, _1)); - - QPID_LOG(debug, *this << " updating consumers."); - ss->getSemanticState().eachConsumer( - boost::bind(&UpdateClient::updateConsumer, this, _1)); - - QPID_LOG(debug, *this << " updating unacknowledged messages."); - broker::DeliveryRecords& drs = ss->getSemanticState().getUnacked(); - std::for_each(drs.begin(), drs.end(), - boost::bind(&UpdateClient::updateUnacked, this, _1, shadowSession)); - - updateTransactionState(ss->getSemanticState()); - - // Adjust command counter for message in progress, will be sent after state update. - boost::intrusive_ptr<Message> inProgress = ss->getMessageInProgress(); - SequenceNumber received = ss->receiverGetReceived().command; - if (inProgress) - --received; - - // Sync the session to ensure all responses from broker have been processed. - shadowSession.sync(); - - // Reset command-sequence state. - proxy.sessionState( - ss->senderGetReplayPoint().command, - ss->senderGetCommandPoint().command, - ss->senderGetIncomplete(), - std::max(received, ss->receiverGetExpected().command), - received, - ss->receiverGetUnknownComplete(), - ss->receiverGetIncomplete(), - ss->getSemanticState().getDtxSelected() - ); - - // Send frames for partial message in progress. - if (inProgress) { - inProgress->getFrames().map(simpl->out); - } - QPID_LOG(debug, *this << " updated session " << sh.getSession()->getId()); -} - -void UpdateClient::updateConsumer( - const broker::SemanticState::ConsumerImpl::shared_ptr& ci) -{ - QPID_LOG(debug, *this << " updating consumer " << ci->getTag() << " on " - << shadowSession.getId()); - - using namespace message; - shadowSession.messageSubscribe( - arg::queue = ci->getQueue()->getName(), - arg::destination = ci->getTag(), - arg::acceptMode = ci->isAckExpected() ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE, - arg::acquireMode = ci->isAcquire() ? ACQUIRE_MODE_PRE_ACQUIRED : ACQUIRE_MODE_NOT_ACQUIRED, - arg::exclusive = ci->isExclusive(), - arg::resumeId = ci->getResumeId(), - arg::resumeTtl = ci->getResumeTtl(), - arg::arguments = ci->getArguments() - ); - shadowSession.messageSetFlowMode(ci->getTag(), ci->getCredit().isWindowMode() ? FLOW_MODE_WINDOW : FLOW_MODE_CREDIT); - shadowSession.messageFlow(ci->getTag(), CREDIT_UNIT_MESSAGE, ci->getCredit().allocated().messages); - shadowSession.messageFlow(ci->getTag(), CREDIT_UNIT_BYTE, ci->getCredit().allocated().bytes); - ClusterConnectionProxy(shadowSession).consumerState( - ci->getTag(), - ci->isBlocked(), - ci->isNotifyEnabled(), - ci->getPosition(), - ci->getCredit().used().messages, - ci->getCredit().used().bytes, - ci->getDeliveryCount() - ); - consumerNumbering.add(ci.get()); - - QPID_LOG(debug, *this << " updated consumer " << ci->getTag() - << " on " << shadowSession.getId()); -} - -void UpdateClient::updateUnacked(const broker::DeliveryRecord& dr, - client::AsyncSession& updateSession) -{ - if (!dr.isEnded() && dr.isAcquired()) { - assert(dr.getMessage().payload); - // If the message is acquired then it is no longer on the - // updatees queue, put it on the update queue for updatee to pick up. - // - MessageUpdater(UPDATE, updateSession, expiry).updateQueuedMessage(dr.getMessage()); - } - ClusterConnectionProxy(updateSession).deliveryRecord( - dr.getQueue()->getName(), - dr.getMessage().position, - dr.getTag(), - dr.getId(), - dr.isAcquired(), - dr.isAccepted(), - dr.isCancelled(), - dr.isComplete(), - dr.isEnded(), - dr.isWindowing(), - dr.getQueue()->isEnqueued(dr.getMessage()), - dr.getCredit() - ); -} - -class TxOpUpdater : public broker::TxOpConstVisitor, public MessageUpdater { - public: - TxOpUpdater(UpdateClient& dc, client::AsyncSession s, ExpiryPolicy& expiry) - : MessageUpdater(UpdateClient::UPDATE, s, expiry), parent(dc), session(s), proxy(s) {} - - void operator()(const broker::DtxAck& ack) { - std::for_each(ack.getPending().begin(), ack.getPending().end(), - boost::bind(&UpdateClient::updateUnacked, &parent, _1, session)); - proxy.dtxAck(); - } - - void operator()(const broker::RecoveredDequeue& rdeq) { - updateMessage(rdeq.getMessage()); - proxy.txEnqueue(rdeq.getQueue()->getName()); - } - - void operator()(const broker::RecoveredEnqueue& renq) { - updateMessage(renq.getMessage()); - proxy.txEnqueue(renq.getQueue()->getName()); - } - - void operator()(const broker::TxAccept& txAccept) { - proxy.txAccept(txAccept.getAcked()); - } - - typedef std::list<Queue::shared_ptr> QueueList; - - void copy(const QueueList& l, Array& a) { - for (QueueList::const_iterator i = l.begin(); i!=l.end(); ++i) - a.push_back(Array::ValuePtr(new Str8Value((*i)->getName()))); - } - - void operator()(const broker::TxPublish& txPub) { - updateMessage(txPub.getMessage()); - assert(txPub.getQueues().empty() || txPub.getPrepared().empty()); - Array qarray(TYPE_CODE_STR8); - copy(txPub.getQueues().empty() ? txPub.getPrepared() : txPub.getQueues(), qarray); - proxy.txPublish(qarray, txPub.delivered); - } - - private: - UpdateClient& parent; - client::AsyncSession session; - ClusterConnectionProxy proxy; -}; - -void UpdateClient::updateBufferRef(const broker::DtxBuffer::shared_ptr& dtx,bool suspended) -{ - ClusterConnectionProxy proxy(shadowSession); - broker::DtxWorkRecord* record = - updaterBroker.getDtxManager().getWork(dtx->getXid()); - proxy.dtxBufferRef(dtx->getXid(), record->indexOf(dtx), suspended); - -} - -void UpdateClient::updateTransactionState(broker::SemanticState& s) { - ClusterConnectionProxy proxy(shadowSession); - proxy.accumulatedAck(s.getAccumulatedAck()); - broker::TxBuffer::shared_ptr tx = s.getTxBuffer(); - broker::DtxBuffer::shared_ptr dtx = s.getDtxBuffer(); - if (dtx) { - updateBufferRef(dtx, false); // Current transaction. - } else if (tx) { - proxy.txStart(); - TxOpUpdater updater(*this, shadowSession, expiry); - tx->accept(updater); - proxy.txEnd(); - } - for (SemanticState::DtxBufferMap::iterator i = s.getSuspendedXids().begin(); - i != s.getSuspendedXids().end(); - ++i) - { - updateBufferRef(i->second, true); - } -} - -void UpdateClient::updateDtxBuffer(const broker::DtxBuffer::shared_ptr& dtx) { - ClusterConnectionProxy proxy(session); - proxy.dtxStart( - dtx->getXid(), dtx->isEnded(), dtx->isSuspended(), dtx->isFailed(), dtx->isExpired()); - TxOpUpdater updater(*this, session, expiry); - dtx->accept(updater); - proxy.dtxEnd(); -} - -void UpdateClient::updateQueueListeners(const boost::shared_ptr<broker::Queue>& queue) { - queue->getListeners().eachListener( - boost::bind(&UpdateClient::updateQueueListener, this, queue->getName(), _1)); -} - -void UpdateClient::updateQueueListener(std::string& q, - const boost::shared_ptr<broker::Consumer>& c) -{ - SemanticState::ConsumerImpl* ci = dynamic_cast<SemanticState::ConsumerImpl*>(c.get()); - size_t n = consumerNumbering[ci]; - if (n >= consumerNumbering.size()) - throw Exception(QPID_MSG("Unexpected listener on queue " << q)); - ClusterConnectionProxy(session).addQueueListener(q, n); -} - -void UpdateClient::updateLinks() { - broker::LinkRegistry& links = updaterBroker.getLinks(); - links.eachLink(boost::bind(&UpdateClient::updateLink, this, _1)); - links.eachBridge(boost::bind(&UpdateClient::updateBridge, this, _1)); -} - -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)); // 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) { - QPID_LOG(debug, *this << " updating bridge " << bridge->getName()); - ClusterConnectionProxy(session).config(encode(*bridge)); -} - -void UpdateClient::updateQueueObservers(const boost::shared_ptr<broker::Queue>& q) -{ - q->eachObserver(boost::bind(&UpdateClient::updateObserver, this, q, _1)); -} - -void UpdateClient::updateObserver(const boost::shared_ptr<broker::Queue>& q, - boost::shared_ptr<broker::QueueObserver> o) -{ - qpid::framing::FieldTable state; - broker::StatefulQueueObserver *so = dynamic_cast<broker::StatefulQueueObserver *>(o.get()); - if (so) { - so->getState( state ); - std::string id(so->getId()); - QPID_LOG(debug, *this << " updating queue " << q->getName() << "'s observer " << id); - ClusterConnectionProxy(session).queueObserverState( q->getName(), id, state ); - } -} - -void UpdateClient::updateDtxManager() { - broker::DtxManager& dtm = updaterBroker.getDtxManager(); - dtm.each(boost::bind(&UpdateClient::updateDtxWorkRecord, this, _1)); -} - -void UpdateClient::updateDtxWorkRecord(const broker::DtxWorkRecord& r) { - QPID_LOG(debug, *this << " updating DTX transaction: " << r.getXid()); - for (size_t i = 0; i < r.size(); ++i) - updateDtxBuffer(r[i]); - ClusterConnectionProxy(session).dtxWorkRecord( - r.getXid(), r.isPrepared(), r.getTimeout()); -} - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/UpdateClient.h b/cpp/src/qpid/cluster/UpdateClient.h deleted file mode 100644 index 481ee357c7..0000000000 --- a/cpp/src/qpid/cluster/UpdateClient.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef QPID_CLUSTER_UPDATECLIENT_H -#define QPID_CLUSTER_UPDATECLIENT_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/cluster/ClusterMap.h" -#include "qpid/cluster/Numbering.h" -#include "qpid/client/Connection.h" -#include "qpid/client/ConnectionSettings.h" -#include "qpid/client/AsyncSession.h" -#include "qpid/broker/SemanticState.h" -#include "qpid/sys/Runnable.h" -#include <boost/shared_ptr.hpp> -#include <iosfwd> - -namespace qpid { - -struct Url; - -namespace broker { - -class Broker; -class Queue; -class Exchange; -class QueueBindings; -struct QueueBinding; -struct QueuedMessage; -class SessionHandler; -class DeliveryRecord; -class SessionState; -class SemanticState; -class Decoder; -class Link; -class Bridge; -class QueueObserver; -class DtxBuffer; -} // namespace broker - -namespace cluster { - -class Cluster; -class Connection; -class ClusterMap; -class Decoder; -class ExpiryPolicy; - -/** - * A client that updates the contents of a local broker to a remote one using AMQP. - */ -class UpdateClient : public sys::Runnable { - public: - static const std::string UPDATE; // Name for special update queue and exchange. - static const std::string X_QPID_EXPIRATION; // Update message expiration - // Flag to remove props/headers that were added by the UpdateClient - static const std::string X_QPID_NO_MESSAGE_PROPS; - static const std::string X_QPID_NO_HEADERS; - - static client::Connection catchUpConnection(); - - UpdateClient(const MemberId& updater, const MemberId& updatee, const Url&, - broker::Broker& donor, const ClusterMap& map, ExpiryPolicy& expiry, - const std::vector<boost::intrusive_ptr<Connection> >&, Decoder&, - const boost::function<void()>& done, - const boost::function<void(const std::exception&)>& fail, - const client::ConnectionSettings& - ); - - ~UpdateClient(); - void update(); - void run(); // Will delete this when finished. - - void updateUnacked(const broker::DeliveryRecord&, client::AsyncSession&); - - private: - void updateQueue(client::AsyncSession&, const boost::shared_ptr<broker::Queue>&); - void updateNonExclusiveQueue(const boost::shared_ptr<broker::Queue>&); - void updateExclusiveQueue(const boost::shared_ptr<broker::Queue>&); - void updateExchange(const boost::shared_ptr<broker::Exchange>&); - void updateMessage(const broker::QueuedMessage&); - void updateMessageTo(const broker::QueuedMessage&, const std::string& queue, client::Session s); - void updateBinding(client::AsyncSession&, const std::string& queue, const broker::QueueBinding& binding); - void updateConnection(const boost::intrusive_ptr<Connection>& connection); - void updateSession(broker::SessionHandler& s); - void updateBufferRef(const broker::DtxBuffer::shared_ptr& dtx, bool suspended); - void updateTransactionState(broker::SemanticState& s); - void updateOutputTask(const sys::OutputTask* task); - void updateConsumer(const broker::SemanticState::ConsumerImpl::shared_ptr&); - void updateQueueListeners(const boost::shared_ptr<broker::Queue>&); - void updateQueueListener(std::string& q, const boost::shared_ptr<broker::Consumer>& c); - void updateManagementSetupState(); - void updateManagementAgent(); - void updateLinks(); - void updateLink(const boost::shared_ptr<broker::Link>&); - void updateBridge(const boost::shared_ptr<broker::Bridge>&); - void updateQueueObservers(const boost::shared_ptr<broker::Queue>&); - void updateObserver(const boost::shared_ptr<broker::Queue>&, boost::shared_ptr<broker::QueueObserver>); - void updateDtxManager(); - void updateDtxBuffer(const boost::shared_ptr<broker::DtxBuffer>& ); - void updateDtxWorkRecord(const broker::DtxWorkRecord&); - - - Numbering<broker::SemanticState::ConsumerImpl*> consumerNumbering; - MemberId updaterId; - MemberId updateeId; - Url updateeUrl; - broker::Broker& updaterBroker; - ClusterMap map; - ExpiryPolicy& expiry; - std::vector<boost::intrusive_ptr<Connection> > connections; - Decoder& decoder; - client::Connection connection, shadowConnection; - client::AsyncSession session, shadowSession; - boost::function<void()> done; - boost::function<void(const std::exception& e)> failed; - client::ConnectionSettings connectionSettings; - - friend std::ostream& operator<<(std::ostream&, const UpdateClient&); -}; - - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_UPDATECLIENT_H*/ diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.cpp b/cpp/src/qpid/cluster/UpdateDataExchange.cpp deleted file mode 100644 index 31d96c67ca..0000000000 --- a/cpp/src/qpid/cluster/UpdateDataExchange.cpp +++ /dev/null @@ -1,77 +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 "UpdateDataExchange.h" -#include "Cluster.h" -#include "qpid/amqp_0_10/Codecs.h" -#include "qpid/broker/Deliverable.h" -#include "qpid/broker/Message.h" -#include "qpid/log/Statement.h" -#include "qpid/management/ManagementAgent.h" -#include "qpid/types/Variant.h" - -namespace qpid { -namespace cluster { - -const std::string UpdateDataExchange::EXCHANGE_NAME("qpid.cluster-update-data"); -const std::string UpdateDataExchange::EXCHANGE_TYPE("qpid.cluster-update-data"); -const std::string UpdateDataExchange::MANAGEMENT_AGENTS_KEY("management-agents"); -const std::string UpdateDataExchange::MANAGEMENT_SCHEMAS_KEY("management-schemas"); -const std::string UpdateDataExchange::MANAGEMENT_DELETED_OBJECTS_KEY("management-deleted-objects"); - -UpdateDataExchange::UpdateDataExchange(Cluster& cluster) : - Exchange(EXCHANGE_NAME, &cluster) -{} - -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; - else if (routingKey == MANAGEMENT_DELETED_OBJECTS_KEY) managementDeletedObjects = data; - else throw Exception( - QPID_MSG("Cluster update-data exchange received unknown routing-key: " - << routingKey)); -} - -void UpdateDataExchange::updateManagementAgent(management::ManagementAgent* agent) { - if (!agent) return; - - framing::Buffer buf1(const_cast<char*>(managementAgents.data()), managementAgents.size()); - agent->importAgents(buf1); - - framing::Buffer buf2(const_cast<char*>(managementSchemas.data()), managementSchemas.size()); - agent->importSchemas(buf2); - - using amqp_0_10::ListCodec; - using types::Variant; - Variant::List encoded; - ListCodec::decode(managementDeletedObjects, encoded); - management::ManagementAgent::DeletedObjectList objects; - for (Variant::List::iterator i = encoded.begin(); i != encoded.end(); ++i) { - objects.push_back(management::ManagementAgent::DeletedObject::shared_ptr( - new management::ManagementAgent::DeletedObject(*i))); - } - agent->importDeletedObjects(objects); -} - - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.h b/cpp/src/qpid/cluster/UpdateDataExchange.h deleted file mode 100644 index f79430f111..0000000000 --- a/cpp/src/qpid/cluster/UpdateDataExchange.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef QPID_CLUSTER_UPDATEDATAEXCHANGE_H -#define QPID_CLUSTER_UPDATEDATAEXCHANGE_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/Exchange.h" -#include "types.h" -#include <iosfwd> - -namespace qpid { - -namespace management { -class ManagementAgent; -} - -namespace cluster { -class Cluster; - -/** - * An exchange used to send data that is to large for a control - * during update. The routing key indicates the type of data. - */ -class UpdateDataExchange : public broker::Exchange -{ - public: - static const std::string EXCHANGE_NAME; - static const std::string EXCHANGE_TYPE; - static const std::string MANAGEMENT_AGENTS_KEY; - static const std::string MANAGEMENT_SCHEMAS_KEY; - static const std::string MANAGEMENT_DELETED_OBJECTS_KEY; - - UpdateDataExchange(Cluster& parent); - - void route(broker::Deliverable& msg); - - // Not implemented - std::string getType() const { return EXCHANGE_TYPE; } - - bool bind(boost::shared_ptr<broker::Queue>, - const std::string&, - const qpid::framing::FieldTable*) - { return false; } - - bool unbind(boost::shared_ptr<broker::Queue>, - const std::string&, - const qpid::framing::FieldTable*) - { return false; } - - bool isBound(boost::shared_ptr<broker::Queue>, - const std::string*, - const qpid::framing::FieldTable*) - { return false; } - - void updateManagementAgent(management::ManagementAgent* agent); - - private: - std::string managementAgents; - std::string managementSchemas; - std::string managementDeletedObjects; -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_UPDATEDATAEXCHANGE_H*/ diff --git a/cpp/src/qpid/cluster/UpdateExchange.cpp b/cpp/src/qpid/cluster/UpdateExchange.cpp deleted file mode 100644 index cb1376004e..0000000000 --- a/cpp/src/qpid/cluster/UpdateExchange.cpp +++ /dev/null @@ -1,68 +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/MessageTransferBody.h" -#include "qpid/framing/FieldTable.h" -#include "qpid/broker/Message.h" -#include "UpdateExchange.h" - -namespace qpid { -namespace cluster { - -using framing::MessageTransferBody; -using framing::DeliveryProperties; -using framing::MessageProperties; -using framing::FieldTable; - -UpdateExchange::UpdateExchange(management::Manageable* parent) - : broker::Exchange(UpdateClient::UPDATE, parent), - broker::FanOutExchange(UpdateClient::UPDATE, parent) {} - - -void UpdateExchange::setProperties(const boost::intrusive_ptr<broker::Message>& msg) { - // Copy exchange name to destination property. - MessageTransferBody* transfer = msg->getMethod<MessageTransferBody>(); - assert(transfer); - const DeliveryProperties* props = msg->getProperties<DeliveryProperties>(); - assert(props); - if (props->hasExchange()) - transfer->setDestination(props->getExchange()); - else - transfer->clearDestinationFlag(); - - // Copy expiration from x-property if present. - if (msg->hasProperties<MessageProperties>()) { - const MessageProperties* mprops = msg->getProperties<MessageProperties>(); - if (mprops->hasApplicationHeaders()) { - const FieldTable& headers = mprops->getApplicationHeaders(); - if (headers.isSet(UpdateClient::X_QPID_EXPIRATION)) { - msg->setExpiration( - sys::AbsTime(sys::EPOCH, headers.getAsInt64(UpdateClient::X_QPID_EXPIRATION))); - msg->removeCustomProperty(UpdateClient::X_QPID_EXPIRATION); - // Erase props/headers that were added by the UpdateClient - if (headers.isSet(UpdateClient::X_QPID_NO_MESSAGE_PROPS)) - msg->eraseProperties<MessageProperties>(); - else if (headers.isSet(UpdateClient::X_QPID_NO_HEADERS)) - msg->clearApplicationHeadersFlag(); - } - } - } -} -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/UpdateExchange.h b/cpp/src/qpid/cluster/UpdateExchange.h deleted file mode 100644 index 9d7d9ee5fc..0000000000 --- a/cpp/src/qpid/cluster/UpdateExchange.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef QPID_CLUSTER_UPDATEEXCHANGE_H -#define QPID_CLUSTER_UPDATEEXCHANGE_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/cluster/UpdateClient.h" -#include "qpid/broker/FanOutExchange.h" - - -namespace qpid { -namespace cluster { - -/** - * A keyless exchange (like fanout exchange) that does not modify - * delivery-properties.exchange but copies it to the MessageTransfer. - */ -class UpdateExchange : public broker::FanOutExchange -{ - public: - UpdateExchange(management::Manageable* parent); - void setProperties(const boost::intrusive_ptr<broker::Message>&); -}; - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_UPDATEEXCHANGE_H*/ diff --git a/cpp/src/qpid/cluster/UpdateReceiver.h b/cpp/src/qpid/cluster/UpdateReceiver.h deleted file mode 100644 index 81ee3a5ffe..0000000000 --- a/cpp/src/qpid/cluster/UpdateReceiver.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef QPID_CLUSTER_UPDATESTATE_H -#define QPID_CLUSTER_UPDATESTATE_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 "Numbering.h" -#include "qpid/broker/SemanticState.h" - -namespace qpid { -namespace cluster { - -/** - * Cluster-wide state used when receiving an update. - */ -class UpdateReceiver { - public: - /** Numbering used to identify Queue listeners as consumers */ - typedef Numbering<boost::shared_ptr<broker::SemanticState::ConsumerImpl> > ConsumerNumbering; - ConsumerNumbering consumerNumbering; - - /** Management-id for the next shadow connection */ - std::string nextShadowMgmtId; - - /** Record the position of a DtxBuffer in the DtxManager (xid + index) - * and the association with a session, either suspended or current. - */ - struct DtxBufferRef { - std::string xid; - uint32_t index; // Index in WorkRecord in DtxManager - bool suspended; // Is this a suspended or current transaction? - broker::SemanticState* semanticState; // Associated session - DtxBufferRef(const std::string& x, uint32_t i, bool s, broker::SemanticState* ss) - : xid(x), index(i), suspended(s), semanticState(ss) {} - }; - typedef std::vector<DtxBufferRef> DtxBuffers; - DtxBuffers dtxBuffers; -}; -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_UPDATESTATE_H*/ diff --git a/cpp/src/qpid/cluster/WatchDogPlugin.cpp b/cpp/src/qpid/cluster/WatchDogPlugin.cpp deleted file mode 100644 index f919041107..0000000000 --- a/cpp/src/qpid/cluster/WatchDogPlugin.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/**@file - - The watchdog plug-in will kill the qpidd broker process if it - becomes stuck for longer than a configured interval. - - If the watchdog plugin is loaded and the --watchdog-interval=N - option is set then the broker starts a watchdog process and signals - it every N/2 seconds. - - The watchdog process runs a very simple program that starts a timer - for N seconds, and resets the timer to N seconds whenever it is - signalled by the broker. If the timer ever reaches 0 the watchdog - kills the broker process (with kill -9) and exits. - - This is useful in a cluster setting because in some insttances - (e.g. while resolving an error) it's possible for a stuck process - to hang other cluster members that are waiting for it to send a - message. Using the watchdog, the stuck process is terminated and - removed fromt the cluster allowing other members to continue and - clients of the stuck process to fail over to other members. - -*/ -#include "config.h" -#include "qpid/Plugin.h" -#include "qpid/Options.h" -#include "qpid/log/Statement.h" -#include "qpid/broker/Broker.h" -#include "qpid/sys/Timer.h" -#include "qpid/sys/Fork.h" -#include <sys/types.h> -#include <sys/wait.h> -#include <signal.h> - -namespace qpid { -namespace cluster { - -using broker::Broker; - -struct Settings { - Settings() : interval(0), watchdogExec(QPID_LIBEXEC_DIR "/qpidd_watchdog") {} - int interval; - std::string watchdogExec; -}; - -struct WatchDogOptions : public qpid::Options { - Settings& settings; - - WatchDogOptions(Settings& s) : settings(s) { - addOptions() - ("watchdog-interval", optValue(settings.interval, "N"), - "broker is automatically killed if it is hung for more than \ - N seconds. 0 disables watchdog.") - ("watchdog-exec", optValue(settings.watchdogExec, ""), - "Path to the qpidd_watchdog executable."); - } -}; - -struct WatchDogTask : public sys::TimerTask { - int pid; - sys::Timer& timer; - int interval; - - WatchDogTask(int pid_, sys::Timer& t, int _interval) - : TimerTask(_interval*sys::TIME_SEC/2,"WatchDog"), pid(pid_), timer(t), interval(_interval) {} - - void fire() { - timer.add (new WatchDogTask(pid, timer, interval)); - QPID_LOG(debug, "Sending keepalive signal to watchdog"); - ::kill(pid, SIGUSR1); - } -}; - -struct WatchDogPlugin : public qpid::Plugin, public qpid::sys::Fork { - Settings settings; - WatchDogOptions options; - Broker* broker; - int watchdogPid; - - WatchDogPlugin() : options(settings), broker(0), watchdogPid(0) {} - - ~WatchDogPlugin() { - if (watchdogPid) ::kill(watchdogPid, SIGTERM); - ::waitpid(watchdogPid, 0, 0); - } - - Options* getOptions() { return &options; } - - void earlyInitialize(qpid::Plugin::Target& target) { - broker = dynamic_cast<Broker*>(&target); - if (broker && settings.interval) { - QPID_LOG(notice, "Starting watchdog process with interval of " << - settings.interval << " seconds"); - fork(); - } - } - - void initialize(Target&) {} - - protected: - - void child() { // Child of fork - std::string interval = boost::lexical_cast<std::string>(settings.interval); - const char* watchdogExec = settings.watchdogExec.c_str(); - ::execl(watchdogExec, watchdogExec, interval.c_str(), NULL); - QPID_LOG(critical, "Failed to exec watchdog program " << watchdogExec); - ::kill(::getppid(), SIGKILL); - exit(1); - } - - void parent(int pid) { // Parent of fork - watchdogPid = pid; - broker->getTimer().add( - new WatchDogTask(watchdogPid, broker->getTimer(), settings.interval)); - // TODO aconway 2009-08-10: to be extra safe, we could monitor - // the watchdog child and re-start it if it exits. - } -}; - -static WatchDogPlugin instance; // Static initialization. - -}} // namespace qpid::cluster diff --git a/cpp/src/qpid/cluster/management-schema.xml b/cpp/src/qpid/cluster/management-schema.xml deleted file mode 100644 index a6292e9113..0000000000 --- a/cpp/src/qpid/cluster/management-schema.xml +++ /dev/null @@ -1,61 +0,0 @@ -<schema package="org.apache.qpid.cluster"> - - <!-- - 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. - --> - - <!-- Type information: - -Numeric types with "_wm" suffix are watermarked numbers. These are compound -values containing a current value, and a low and high water mark for the reporting -interval. The low and high water marks are set to the current value at the -beginning of each interval and track the minimum and maximum values of the statistic -over the interval respectively. - -Access rights for configuration elements: - -RO => Read Only -RC => Read/Create, can be set at create time only, read-only thereafter -RW => Read/Write - -If access rights are omitted for a property, they are assumed to be RO. - - --> - - <class name="Cluster"> - <property name="brokerRef" type="objId" references="Broker" access="RC" index="y" parentRef="y"/> - <property name="clusterName" type="sstr" access="RC" desc="Name of cluster this server is a member of"/> - <property name="clusterID" type="sstr" access="RO" desc="Globally unique ID (UUID) for this cluster instance"/> - <property name="memberID" type="sstr" access="RO" desc="ID of this member of the cluster"/> - <property name="publishedURL" type="sstr" access="RC" desc="URL this node advertizes itself as"/> - <property name="clusterSize" type="uint16" access="RO" desc="Number of brokers currently in the cluster"/> - <property name="status" type="sstr" access="RO" desc="Cluster node status (STALLED,ACTIVE,JOINING)"/> - <property name="members" type="lstr" access="RO" desc="List of member URLs delimited by ';'"/> - <property name="memberIDs" type="lstr" access="RO" desc="List of member IDs delimited by ';'"/> - - <method name="stopClusterNode"> - <arg name="brokerId" type="sstr" dir="I"/> - </method> - <method name="stopFullCluster"/> - - </class> - - - -</schema> - diff --git a/cpp/src/qpid/cluster/qpidd_watchdog.cpp b/cpp/src/qpid/cluster/qpidd_watchdog.cpp deleted file mode 100644 index 51c5ed4b3f..0000000000 --- a/cpp/src/qpid/cluster/qpidd_watchdog.cpp +++ /dev/null @@ -1,63 +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. - * - */ - -/** @file helper executable for WatchDogPlugin.cpp */ - -#include <sys/types.h> -#include <sys/time.h> -#include <signal.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <limits.h> - -long timeout; - -void killParent(int) { - ::kill(getppid(), SIGKILL); - ::fprintf(stderr, "Watchdog killed unresponsive broker, pid=%d\n", ::getppid()); - ::exit(1); -} - -void resetTimer(int) { - struct ::itimerval itval = { { 0, 0 }, { timeout, 0 } }; - if (::setitimer(ITIMER_REAL, &itval, 0) !=0) { - ::perror("Watchdog failed to set timer"); - killParent(0); - ::exit(1); - } -} - -/** Simple watchdog program: kill parent process if timeout - * expires without a SIGUSR1. - * Will be killed with SIGHUP when parent shuts down. - * Args: timeout in seconds. - */ -int main(int argc, char** argv) { - if(argc != 2 || (timeout = atoi(argv[1])) == 0) { - ::fprintf(stderr, "Usage: %s <timeout_seconds>\n", argv[0]); - ::exit(1); - } - ::signal(SIGUSR1, resetTimer); - ::signal(SIGALRM, killParent); - resetTimer(0); - while (true) { sleep(INT_MAX); } -} diff --git a/cpp/src/qpid/cluster/types.h b/cpp/src/qpid/cluster/types.h deleted file mode 100644 index c8ffb0b804..0000000000 --- a/cpp/src/qpid/cluster/types.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef QPID_CLUSTER_TYPES_H -#define QPID_CLUSTER_TYPES_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 "config.h" -#include "qpid/Url.h" -#include "qpid/RefCounted.h" -#include "qpid/sys/IntegerTypes.h" -#include <boost/intrusive_ptr.hpp> -#include <utility> -#include <iosfwd> -#include <string> - -extern "C" { -#if defined (HAVE_OPENAIS_CPG_H) -# include <openais/cpg.h> - -// Provide translations back to the deprecated definitions in openais -typedef cpg_error_t cs_error_t; -#define CS_DISPATCH_ONE CPG_DISPATCH_ONE -#define CS_DISPATCH_ALL CPG_DISPATCH_ALL -#define CS_DISPATCH_BLOCKING CPG_DISPATCH_BLOCKING -#define CS_FLOW_CONTROL_DISABLED CPG_FLOW_CONTROL_DISABLED -#define CS_FLOW_CONTROL_ENABLED CPG_FLOW_CONTROL_ENABLED -#define CS_OK CPG_OK -#define CS_ERR_LIBRARY CPG_ERR_LIBRARY -#define CS_ERR_TIMEOUT CPG_ERR_TIMEOUT -#define CS_ERR_TRY_AGAIN CPG_ERR_TRY_AGAIN -#define CS_ERR_INVALID_PARAM CPG_ERR_INVALID_PARAM -#define CS_ERR_NO_MEMORY CPG_ERR_NO_MEMORY -#define CS_ERR_BAD_HANDLE CPG_ERR_BAD_HANDLE -#define CS_ERR_BUSY CPG_ERR_BUSY -#define CS_ERR_ACCESS CPG_ERR_ACCESS -#define CS_ERR_NOT_EXIST CPG_ERR_NOT_EXIST -#define CS_ERR_EXIST CPG_ERR_EXIST -#define CS_ERR_NOT_SUPPORTED CPG_ERR_NOT_SUPPORTED -#define CS_ERR_SECURITY CPG_ERR_SECURITY -#define CS_ERR_TOO_MANY_GROUPS CPG_ERR_TOO_MANY_GROUPS - -#elif defined (HAVE_COROSYNC_CPG_H) -# include <corosync/cpg.h> -#else -# error "No cpg.h header file available" -#endif -} - -namespace qpid { -namespace cluster { - -class Connection; -typedef boost::intrusive_ptr<Connection> ConnectionPtr; - -/** Types of cluster event. */ -enum EventType { DATA, CONTROL }; - -/** first=node-id, second=pid */ -struct MemberId : std::pair<uint32_t, uint32_t> { - MemberId(uint64_t n=0) : std::pair<uint32_t,uint32_t>( n >> 32, n & 0xffffffff) {} - MemberId(uint32_t node, uint32_t pid) : std::pair<uint32_t,uint32_t>(node, pid) {} - MemberId(const cpg_address& caddr) : std::pair<uint32_t,uint32_t>(caddr.nodeid, caddr.pid) {} - MemberId(const std::string&); // Decode from string. - uint32_t getNode() const { return first; } - uint32_t getPid() const { return second; } - operator uint64_t() const { return (uint64_t(first)<<32ull) + second; } - - // MemberId as byte string, network byte order. Not human readable. - std::string str() const; -}; - -inline bool operator==(const cpg_address& caddr, const MemberId& id) { return id == MemberId(caddr); } - -std::ostream& operator<<(std::ostream&, const MemberId&); - -struct ConnectionId : public std::pair<MemberId, uint64_t> { - ConnectionId(const MemberId& m=MemberId(), uint64_t c=0) : std::pair<MemberId, uint64_t> (m,c) {} - ConnectionId(uint64_t m, uint64_t c) : std::pair<MemberId, uint64_t>(MemberId(m), c) {} - MemberId getMember() const { return first; } - uint64_t getNumber() const { return second; } -}; - -std::ostream& operator<<(std::ostream&, const ConnectionId&); - -std::ostream& operator<<(std::ostream&, EventType); - -}} // namespace qpid::cluster - -#endif /*!QPID_CLUSTER_TYPES_H*/ diff --git a/cpp/src/qpid/ha/Backup.cpp b/cpp/src/qpid/ha/Backup.cpp index bac6fd23c8..6852a58b0c 100644 --- a/cpp/src/qpid/ha/Backup.cpp +++ b/cpp/src/qpid/ha/Backup.cpp @@ -35,6 +35,7 @@ #include "qpid/framing/MessageTransferBody.h" #include "qpid/sys/SystemInfo.h" #include "qpid/types/Variant.h" +#include "qpid/log/Statement.h" namespace qpid { namespace ha { @@ -51,35 +52,15 @@ Backup::Backup(HaBroker& hb, const Settings& s) : if (!s.brokerUrl.empty()) initialize(Url(s.brokerUrl)); } -bool Backup::isSelf(const Address& a) const { - return sys::SystemInfo::isLocalHost(a.host) && - a.port == haBroker.getBroker().getPort(a.protocol); -} - -// Remove my own address from the URL if possible. -// This isn't 100% reliable given the many ways to specify a host, -// but should work in most cases. We have additional measures to prevent -// self-connection in ConnectionObserver -Url Backup::removeSelf(const Url& brokers) const { - Url url; - for (Url::const_iterator i = brokers.begin(); i != brokers.end(); ++i) - if (!isSelf(*i)) url.push_back(*i); - if (url.empty()) - throw Url::Invalid(logPrefix+"Failover URL is empty"); - QPID_LOG(debug, logPrefix << "Failover URL (excluding self): " << url); - return url; -} - void Backup::initialize(const Url& brokers) { if (brokers.empty()) throw Url::Invalid("HA broker URL is empty"); QPID_LOG(info, logPrefix << "Connecting to cluster, broker URL: " << brokers); - Url url = removeSelf(brokers); - string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol; + string protocol = brokers[0].protocol.empty() ? "tcp" : brokers[0].protocol; types::Uuid uuid(true); // Declare the link std::pair<Link::shared_ptr, bool> result = broker.getLinks().declare( broker::QPID_NAME_PREFIX + string("ha.link.") + uuid.str(), - url[0].host, url[0].port, protocol, + brokers[0].host, brokers[0].port, protocol, false, // durable settings.mechanism, settings.username, settings.password, false); // no amq.failover - don't want to use client URL. @@ -90,7 +71,7 @@ void Backup::initialize(const Url& brokers) { replicator->initialize(); broker.getExchanges().registerExchange(replicator); } - link->setUrl(url); // Outside the lock, once set link doesn't change. + link->setUrl(brokers); // Outside the lock, once set link doesn't change. } Backup::~Backup() { @@ -107,10 +88,8 @@ void Backup::setBrokerUrl(const Url& url) { sys::Mutex::ScopedLock l(lock); linkSet = link; } - if (linkSet) { - QPID_LOG(info, logPrefix << "Broker URL set to: " << url); - link->setUrl(removeSelf(url)); // Outside lock, once set link doesn't change - } + if (linkSet) + link->setUrl(url); // Outside lock, once set link doesn't change else initialize(url); // Deferred initialization } diff --git a/cpp/src/qpid/ha/Backup.h b/cpp/src/qpid/ha/Backup.h index 1233a473ec..4f2d5babde 100644 --- a/cpp/src/qpid/ha/Backup.h +++ b/cpp/src/qpid/ha/Backup.h @@ -53,8 +53,6 @@ class Backup void setStatus(BrokerStatus); private: - bool isSelf(const Address& a) const; - Url removeSelf(const Url&) const; void initialize(const Url&); std::string logPrefix; diff --git a/cpp/src/qpid/ha/BackupConnectionExcluder.h b/cpp/src/qpid/ha/BackupConnectionExcluder.h index ef537ab90a..5a67cde922 100644 --- a/cpp/src/qpid/ha/BackupConnectionExcluder.h +++ b/cpp/src/qpid/ha/BackupConnectionExcluder.h @@ -36,7 +36,7 @@ class BackupConnectionExcluder : public broker::ConnectionObserver { public: void opened(broker::Connection& connection) { - QPID_LOG(debug, "Backup broker rejected connection "+connection.getMgmtId()); + QPID_LOG(debug, "Backup: Rejected connection "+connection.getMgmtId()); connection.abort(); } diff --git a/cpp/src/qpid/ha/BrokerReplicator.cpp b/cpp/src/qpid/ha/BrokerReplicator.cpp index c8c4a42d72..fe32753b4e 100644 --- a/cpp/src/qpid/ha/BrokerReplicator.cpp +++ b/cpp/src/qpid/ha/BrokerReplicator.cpp @@ -24,7 +24,9 @@ #include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" #include "qpid/broker/Queue.h" +#include "qpid/broker/QueueSettings.h" #include "qpid/broker/Link.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/FieldTable.h" #include "qpid/log/Statement.h" #include "qpid/amqp_0_10/Codecs.h" @@ -117,11 +119,6 @@ const string _QUERY_REQUEST("_query_request"); const string BROKER("broker"); const string MEMBERS("members"); -bool isQMFv2(const Message& message) { - const framing::MessageProperties* props = message.getProperties<framing::MessageProperties>(); - return props && props->getAppId() == QMF2; -} - template <class T> bool match(Variant::Map& schema) { return T::match(schema[CLASS_NAME], schema[PACKAGE_NAME]); } @@ -219,7 +216,7 @@ void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionH link->getRemoteAddress(primary); string queueName = bridge.getQueueName(); - QPID_LOG(info, logPrefix << (initialized ? "Connecting" : "Failing over") + QPID_LOG(info, logPrefix << (initialized ? "Failing over" : "Connecting") << " to primary " << primary << " status:" << printable(haBroker.getStatus())); initialized = true; @@ -253,18 +250,15 @@ void BrokerReplicator::route(Deliverable& msg) { haBroker.setStatus(CATCHUP); QPID_LOG(notice, logPrefix << "Connected to primary " << primary); } - - const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders(); - const MessageProperties* messageProperties = msg.getMessage().getProperties<MessageProperties>(); Variant::List list; try { - if (!isQMFv2(msg.getMessage()) || !headers || !messageProperties) + if (!qpid::broker::amqp_0_10::MessageTransfer::isQMFv2(msg.getMessage())) throw Exception("Unexpected message, not QMF2 event or query response."); // decode as list - string content = msg.getMessage().getFrames().getContent(); - amqp_0_10::ListCodec::decode(content, list); - QPID_LOG(trace, "Broker replicator received: " << *messageProperties); - if (headers->getAsString(QMF_CONTENT) == EVENT) { + string content = msg.getMessage().getContent(); + qpid::amqp_0_10::ListCodec::decode(content, list); + + if (msg.getMessage().getPropertyAsString(QMF_CONTENT) == EVENT) { for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) { Variant::Map& map = i->asMap(); QPID_LOG(trace, "Broker replicator event: " << map); @@ -278,20 +272,20 @@ void BrokerReplicator::route(Deliverable& msg) { else if (match<EventUnbind>(schema)) doEventUnbind(values); else if (match<EventMembersUpdate>(schema)) doEventMembersUpdate(values); } - } else if (headers->getAsString(QMF_OPCODE) == QUERY_RESPONSE) { + } else if (msg.getMessage().getPropertyAsString(QMF_OPCODE) == QUERY_RESPONSE) { for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) { Variant::Map& map = i->asMap(); QPID_LOG(trace, "Broker replicator response: " << map); string type = map[SCHEMA_ID].asMap()[CLASS_NAME].asString(); Variant::Map& values = map[VALUES].asMap(); framing::FieldTable args; - amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); + qpid::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 if (type == HA_BROKER) doResponseHaBroker(values); } - if (messageProperties->getCorrelationId() == EXCHANGE && !headers->isSet(PARTIAL)) { + if (qpid::broker::amqp_0_10::MessageTransfer::isLastQMFResponse(msg.getMessage(), EXCHANGE)) { // We have received all of the exchange response. alternates.clear(); } @@ -309,13 +303,11 @@ void BrokerReplicator::doEventQueueDeclare(Variant::Map& values) { Variant::Map argsMap = asMapVoid(values[ARGS]); bool autoDel = values[AUTODEL].asBool(); bool excl = values[EXCL].asBool(); - if (values[DISP] == CREATED && - replicationTest.isReplicated(CONFIGURATION, argsMap, autoDel, excl)) - { + if (values[DISP] == CREATED && replicationTest.isReplicated(CONFIGURATION, argsMap, autoDel, excl)) { string name = values[QNAME].asString(); - QPID_LOG(debug, logPrefix << "Queue declare event: " << name); + QueueSettings settings(values[DURABLE].asBool(), values[AUTODEL].asBool()); framing::FieldTable args; - amqp_0_10::translate(argsMap, args); + qpid::amqp_0_10::translate(argsMap, args); // If we already have a queue with this name, replace it. // The queue was definitely created on the primary. if (broker.getQueues().find(name)) { @@ -323,10 +315,17 @@ void BrokerReplicator::doEventQueueDeclare(Variant::Map& values) { broker.getQueues().destroy(name); stopQueueReplicator(name); } - boost::shared_ptr<Queue> queue = createQueue( - name, values[DURABLE].asBool(), autoDel, args, values[ALTEX].asString()); - assert(queue); // Should be created since we destroed the previous queue above. - if (queue) startQueueReplicator(queue); + settings.populate(args, settings.storeSettings); + std::pair<boost::shared_ptr<Queue>, bool> result = + broker.createQueue( + name, + settings, + 0 /*i.e. no owner regardless of exclusivity on master*/, + values[ALTEX].asString(), + userId, + remoteHost); + assert(result.second); // Should be true since we destroyed existing queue above + startQueueReplicator(result.first); } } @@ -343,7 +342,7 @@ 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 && replicationTest.replicateLevel(queue->getSettings())) { + if (queue && replicationTest.replicateLevel(queue->getSettings().storeSettings)) { QPID_LOG(debug, logPrefix << "Queue delete event: " << name); stopQueueReplicator(name); broker.deleteQueue(name, userId, remoteHost); @@ -357,7 +356,7 @@ void BrokerReplicator::doEventExchangeDeclare(Variant::Map& values) { string name = values[EXNAME].asString(); QPID_LOG(debug, logPrefix << "Exchange declare event: " << name); framing::FieldTable args; - amqp_0_10::translate(argsMap, args); + qpid::amqp_0_10::translate(argsMap, args); // If we already have a exchange with this name, replace it. // The exchange was definitely created on the primary. if (broker.getExchanges().find(name)) { @@ -391,10 +390,10 @@ void BrokerReplicator::doEventBind(Variant::Map& values) { // We only replicate binds for a replicated queue to replicated // exchange that both exist locally. if (exchange && replicationTest.replicateLevel(exchange->getArgs()) && - queue && replicationTest.replicateLevel(queue->getSettings())) + queue && replicationTest.replicateLevel(queue->getSettings().storeSettings)) { framing::FieldTable args; - amqp_0_10::translate(asMapVoid(values[ARGS]), args); + qpid::amqp_0_10::translate(asMapVoid(values[ARGS]), args); string key = values[KEY].asString(); QPID_LOG(debug, logPrefix << "Bind event: exchange=" << exchange->getName() << " queue=" << queue->getName() @@ -411,10 +410,10 @@ void BrokerReplicator::doEventUnbind(Variant::Map& values) { // We only replicate unbinds for a replicated queue to replicated // exchange that both exist locally. if (exchange && replicationTest.replicateLevel(exchange->getArgs()) && - queue && replicationTest.replicateLevel(queue->getSettings())) + queue && replicationTest.replicateLevel(queue->getSettings().storeSettings)) { framing::FieldTable args; - amqp_0_10::translate(asMapVoid(values[ARGS]), args); + qpid::amqp_0_10::translate(asMapVoid(values[ARGS]), args); string key = values[KEY].asString(); QPID_LOG(debug, logPrefix << "Unbind event: exchange=" << exchange->getName() << " queue=" << queue->getName() @@ -455,7 +454,7 @@ void BrokerReplicator::doResponseQueue(Variant::Map& values) { string name(values[NAME].asString()); QPID_LOG(debug, logPrefix << "Queue response: " << name); framing::FieldTable args; - amqp_0_10::translate(argsMap, args); + qpid::amqp_0_10::translate(argsMap, args); boost::shared_ptr<Queue> queue = createQueue(name, values[DURABLE].asBool(), values[AUTODELETE].asBool(), args, getAltExchange(values[ALTEXCHANGE])); @@ -470,11 +469,12 @@ void BrokerReplicator::doResponseExchange(Variant::Map& values) { string name = values[NAME].asString(); QPID_LOG(debug, logPrefix << "Exchange response: " << name); framing::FieldTable args; - amqp_0_10::translate(argsMap, args); + qpid::amqp_0_10::translate(argsMap, args); boost::shared_ptr<Exchange> exchange = createExchange( name, values[TYPE].asString(), values[DURABLE].asBool(), args, getAltExchange(values[ALTEXCHANGE])); - QPID_LOG_IF(debug, !exchange, logPrefix << "Exchange already exists: " << name); + // It is normal for the exchange to already exist if we are failing over. + QPID_LOG_IF(debug, !exchange, logPrefix << "Exchange already replicated: " << name); } namespace { @@ -506,14 +506,14 @@ void BrokerReplicator::doResponseBind(Variant::Map& values) { // Automatically replicate binding if queue and exchange exist and are replicated if (exchange && replicationTest.replicateLevel(exchange->getArgs()) && - queue && replicationTest.replicateLevel(queue->getSettings())) + queue && replicationTest.replicateLevel(queue->getSettings().storeSettings)) { string key = values[KEY].asString(); QPID_LOG(debug, logPrefix << "Bind response: exchange:" << exName << " queue:" << qName << " key:" << key); framing::FieldTable args; - amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); + qpid::amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); exchange->bind(queue, key, &args); } } @@ -543,7 +543,7 @@ void BrokerReplicator::doResponseHaBroker(Variant::Map& values) { void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue) { - if (replicationTest.replicateLevel(queue->getSettings()) == ALL) { + if (replicationTest.replicateLevel(queue->getSettings().storeSettings) == ALL) { boost::shared_ptr<QueueReplicator> qr( new QueueReplicator(haBroker, queue, link)); if (!broker.getExchanges().registerExchange(qr)) @@ -569,14 +569,14 @@ boost::shared_ptr<Queue> BrokerReplicator::createQueue( const qpid::framing::FieldTable& arguments, const std::string& alternateExchange) { + QueueSettings settings(durable, autodelete); + settings.populate(arguments, settings.storeSettings); std::pair<boost::shared_ptr<Queue>, bool> result = broker.createQueue( name, - durable, - autodelete, - 0, // no owner regardless of exclusivity on primary + settings, + 0,// no owner regardless of exclusivity on primary string(), // Set alternate exchange below - arguments, userId, remoteHost); if (result.second) { diff --git a/cpp/src/qpid/ha/ConnectionObserver.cpp b/cpp/src/qpid/ha/ConnectionObserver.cpp index 3f7a1710d9..81ba3e4301 100644 --- a/cpp/src/qpid/ha/ConnectionObserver.cpp +++ b/cpp/src/qpid/ha/ConnectionObserver.cpp @@ -32,7 +32,7 @@ namespace ha { ConnectionObserver::ConnectionObserver(HaBroker& hb, const types::Uuid& uuid) : haBroker(hb), logPrefix("Connections: "), self(uuid) {} -bool ConnectionObserver::getBrokerInfo(broker::Connection& connection, BrokerInfo& info) { +bool ConnectionObserver::getBrokerInfo(const broker::Connection& connection, BrokerInfo& info) { framing::FieldTable ft; if (connection.getClientProperties().getTable(ConnectionObserver::BACKUP_TAG, ft)) { info = BrokerInfo(ft); @@ -51,21 +51,23 @@ ConnectionObserver::ObserverPtr ConnectionObserver::getObserver() { return observer; } +bool ConnectionObserver::isSelf(const broker::Connection& connection) { + BrokerInfo info; + return getBrokerInfo(connection, info) && info.getSystemId() == self; +} + void ConnectionObserver::opened(broker::Connection& connection) { try { if (connection.isLink()) return; // Allow outgoing links. if (connection.getClientProperties().isSet(ADMIN_TAG)) { - QPID_LOG(debug, logPrefix << "Allowing admin connection: " + QPID_LOG(debug, logPrefix << "Accepted admin connection: " << connection.getMgmtId()); return; // No need to call observer, always allow admins. } - BrokerInfo info; // Avoid self connections. - if (getBrokerInfo(connection, info)) { - if (info.getSystemId() == self) { - QPID_LOG(debug, "HA broker rejected self connection "+connection.getMgmtId()); - connection.abort(); - } - + if (isSelf(connection)) { // Reject self connections + QPID_LOG(debug, logPrefix << "Rejected self connection "+connection.getMgmtId()); + connection.abort(); + return; } ObserverPtr o(getObserver()); if (o) o->opened(connection); @@ -77,8 +79,8 @@ void ConnectionObserver::opened(broker::Connection& connection) { } void ConnectionObserver::closed(broker::Connection& connection) { + if (isSelf(connection)) return; // Ignore closing of self connections. try { - BrokerInfo info; ObserverPtr o(getObserver()); if (o) o->closed(connection); } diff --git a/cpp/src/qpid/ha/ConnectionObserver.h b/cpp/src/qpid/ha/ConnectionObserver.h index 5c1dabe8f8..e3a6d1154a 100644 --- a/cpp/src/qpid/ha/ConnectionObserver.h +++ b/cpp/src/qpid/ha/ConnectionObserver.h @@ -51,7 +51,7 @@ class ConnectionObserver : public broker::ConnectionObserver static const std::string ADMIN_TAG; static const std::string BACKUP_TAG; - static bool getBrokerInfo(broker::Connection& connection, BrokerInfo& info); + static bool getBrokerInfo(const broker::Connection& connection, BrokerInfo& info); ConnectionObserver(HaBroker& haBroker, const types::Uuid& self); @@ -62,6 +62,8 @@ class ConnectionObserver : public broker::ConnectionObserver void closed(broker::Connection& connection); private: + bool isSelf(const broker::Connection&); + sys::Mutex lock; HaBroker& haBroker; std::string logPrefix; diff --git a/cpp/src/qpid/ha/HaBroker.cpp b/cpp/src/qpid/ha/HaBroker.cpp index d126639813..ffbcb684bc 100644 --- a/cpp/src/qpid/ha/HaBroker.cpp +++ b/cpp/src/qpid/ha/HaBroker.cpp @@ -83,7 +83,11 @@ void HaBroker::initialize() { // FIXME aconway 2012-07-19: assumes there's a TCP transport with a meaningful port. brokerInfo = BrokerInfo( - broker.getSystem()->getNodeName(), broker.getPort(broker::Broker::TCP_TRANSPORT), systemId); + broker.getSystem()->getNodeName(), + broker.getPort(broker::Broker::TCP_TRANSPORT), + systemId); + + QPID_LOG(notice, logPrefix << "Initializing: " << brokerInfo); // Set up the management object. ManagementAgent* ma = broker.getManagementAgent(); @@ -111,8 +115,6 @@ void HaBroker::initialize() { if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl)); - QPID_LOG(notice, logPrefix << "Initializing: " << brokerInfo); - // NOTE: lock is not needed in a constructor, but create one // to pass to functions that have a ScopedLock parameter. Mutex::ScopedLock l(lock); @@ -226,6 +228,7 @@ void HaBroker::setBrokerUrl(const Url& url) { if (url.empty()) throw Url::Invalid("HA broker URL is empty"); brokerUrl = url; mgmtObject->set_brokersUrl(brokerUrl.str()); + QPID_LOG(info, logPrefix << "Broker URL set to: " << url); if (backup.get()) backup->setBrokerUrl(brokerUrl); // Updating broker URL also updates defaulted client URL: if (clientUrl.empty()) updateClientUrl(l); @@ -292,6 +295,7 @@ void HaBroker::statusChanged(Mutex::ScopedLock& l) { } void HaBroker::membershipUpdated(Mutex::ScopedLock&) { + QPID_LOG(info, logPrefix << "Membership changed: " << membership); Variant::List brokers = membership.asList(); mgmtObject->set_members(brokers); broker.getManagementAgent()->raiseEvent(_qmf::EventMembersUpdate(brokers)); @@ -321,14 +325,14 @@ void HaBroker::resetMembership(const BrokerInfo& b) { void HaBroker::addBroker(const BrokerInfo& b) { Mutex::ScopedLock l(lock); membership.add(b); - QPID_LOG(debug, logPrefix << "Membership add: " << b << " now: " << membership); + QPID_LOG(debug, logPrefix << "Membership add: " << b); membershipUpdated(l); } void HaBroker::removeBroker(const Uuid& id) { Mutex::ScopedLock l(lock); membership.remove(id); - QPID_LOG(debug, logPrefix << "Membership remove: " << id << " now: " << membership); + QPID_LOG(debug, logPrefix << "Membership remove: " << id); membershipUpdated(l); } diff --git a/cpp/src/qpid/ha/HaBroker.h b/cpp/src/qpid/ha/HaBroker.h index 0ffc152097..7dabe6e35b 100644 --- a/cpp/src/qpid/ha/HaBroker.h +++ b/cpp/src/qpid/ha/HaBroker.h @@ -97,6 +97,8 @@ class HaBroker : public management::Manageable void addBroker(const BrokerInfo& b); // Add a broker to the membership. void removeBroker(const types::Uuid& id); // Remove a broker from membership. + types::Uuid getSystemId() const { return systemId; } + private: void setClientUrl(const Url&); void setBrokerUrl(const Url&); diff --git a/cpp/src/qpid/ha/Membership.cpp b/cpp/src/qpid/ha/Membership.cpp index cc2906dd8f..74580f9b1e 100644 --- a/cpp/src/qpid/ha/Membership.cpp +++ b/cpp/src/qpid/ha/Membership.cpp @@ -66,7 +66,7 @@ types::Variant::List Membership::asList() const { BrokerInfo::Set Membership::otherBackups() const { BrokerInfo::Set result; for (BrokerInfo::Map::const_iterator i = brokers.begin(); i != brokers.end(); ++i) - if (isBackup(i->second.getStatus()) && i->second.getSystemId() != self) + if (i->second.getStatus() == READY && i->second.getSystemId() != self) result.insert(i->second); return result; } diff --git a/cpp/src/qpid/ha/Membership.h b/cpp/src/qpid/ha/Membership.h index 3bd8653a64..8406dccd5d 100644 --- a/cpp/src/qpid/ha/Membership.h +++ b/cpp/src/qpid/ha/Membership.h @@ -47,7 +47,7 @@ class Membership void add(const BrokerInfo& b); void remove(const types::Uuid& id); bool contains(const types::Uuid& id); - /** Return IDs of all backups other than self */ + /** Return IDs of all READY backups other than self */ BrokerInfo::Set otherBackups() const; void assign(const types::Variant::List&); diff --git a/cpp/src/qpid/ha/Primary.cpp b/cpp/src/qpid/ha/Primary.cpp index 69c94bfc7d..e4bf9671b8 100644 --- a/cpp/src/qpid/ha/Primary.cpp +++ b/cpp/src/qpid/ha/Primary.cpp @@ -180,7 +180,7 @@ void Primary::readyReplica(const ReplicatingSubscription& rs) { void Primary::queueCreate(const QueuePtr& q) { // Throw if there is an invalid replication level in the queue settings. - haBroker.getReplicationTest().replicateLevel(q->getSettings()); + haBroker.getReplicationTest().replicateLevel(q->getSettings().storeSettings); Mutex::ScopedLock l(lock); for (BackupMap::iterator i = backups.begin(); i != backups.end(); ++i) { i->second->queueCreate(q); @@ -201,6 +201,7 @@ void Primary::opened(broker::Connection& connection) { Mutex::ScopedLock l(lock); BackupMap::iterator i = backups.find(info.getSystemId()); if (i == backups.end()) { + QPID_LOG(debug, logPrefix << "New backup connected: " << info); boost::shared_ptr<RemoteBackup> backup( new RemoteBackup(info, haBroker.getReplicationTest(), true)); { @@ -209,7 +210,6 @@ void Primary::opened(broker::Connection& connection) { backup->setInitialQueues(haBroker.getBroker().getQueues(), false); } backups[info.getSystemId()] = backup; - QPID_LOG(debug, logPrefix << "New backup connected: " << info); } else { QPID_LOG(debug, logPrefix << "Known backup connected: " << info); @@ -225,6 +225,12 @@ void Primary::opened(broker::Connection& connection) { } void Primary::closed(broker::Connection& connection) { + // NOTE: It is possible for a backup connection to be rejected while we are + // a backup, but closed() is called after we have become primary. + // + // For this reason we do not remove from the backups map here, the backups + // map holds all the backups we know about whether connected or not. + // Mutex::ScopedLock l(lock); BrokerInfo info; if (ha::ConnectionObserver::getBrokerInfo(connection, info)) { @@ -233,12 +239,6 @@ void Primary::closed(broker::Connection& connection) { BackupMap::iterator i = backups.find(info.getSystemId()); if (i != backups.end()) i->second->setConnected(false); } - // NOTE: we do not remove from the backups map here, the backups map holds - // all the backups we know about whether connected or not. - // - // It is possible for a backup connection to be rejected while we are a backup, - // but the closed is seen after we have become primary. Removing the entry - // from backups in this case would be incorrect. } diff --git a/cpp/src/qpid/ha/QueueGuard.cpp b/cpp/src/qpid/ha/QueueGuard.cpp index a30ab1f73c..77e1f81a38 100644 --- a/cpp/src/qpid/ha/QueueGuard.cpp +++ b/cpp/src/qpid/ha/QueueGuard.cpp @@ -39,10 +39,10 @@ class QueueGuard::QueueObserver : public broker::QueueObserver { public: QueueObserver(QueueGuard& g) : guard(g) {} - void enqueued(const broker::QueuedMessage& qm) { guard.enqueued(qm); } - void dequeued(const broker::QueuedMessage& qm) { guard.dequeued(qm); } - void acquired(const broker::QueuedMessage&) {} - void requeued(const broker::QueuedMessage&) {} + void enqueued(const broker::Message& m) { guard.enqueued(m); } + void dequeued(const broker::Message& m) { guard.dequeued(m); } + void acquired(const broker::Message&) {} + void requeued(const broker::Message&) {} private: QueueGuard& guard; }; @@ -64,39 +64,47 @@ QueueGuard::QueueGuard(broker::Queue& q, const BrokerInfo& info) QueueGuard::~QueueGuard() { cancel(); } // NOTE: Called with message lock held. -void QueueGuard::enqueued(const QueuedMessage& qm) { - assert(qm.queue == &queue); +void QueueGuard::enqueued(const Message& m) { // Delay completion - QPID_LOG(trace, logPrefix << "Delayed completion of " << qm); - qm.payload->getIngressCompletion().startCompleter(); + QPID_LOG(trace, logPrefix << "Delayed completion of " << m); + m.getIngressCompletion()->startCompleter(); { Mutex::ScopedLock l(lock); - assert(!delayed.contains(qm.position)); - delayed += qm.position; + if (!delayed.insert(Delayed::value_type(m.getSequence(), m.getIngressCompletion())).second) { + QPID_LOG(critical, logPrefix << "Second enqueue for message with sequence " << m.getSequence()); + assert(false); + } } } // NOTE: Called with message lock held. -void QueueGuard::dequeued(const QueuedMessage& qm) { - assert(qm.queue == &queue); - QPID_LOG(trace, logPrefix << "Dequeued " << qm); +void QueueGuard::dequeued(const Message& m) { + QPID_LOG(trace, logPrefix << "Dequeued " << m); ReplicatingSubscription* rs=0; { Mutex::ScopedLock l(lock); rs = subscription; } - if (rs) rs->dequeued(qm); - complete(qm); + if (rs) rs->dequeued(m); + complete(m.getSequence()); +} + +void QueueGuard::completeRange(Delayed::iterator begin, Delayed::iterator end) { + for (Delayed::iterator i = begin; i != end; ++i) { + QPID_LOG(trace, logPrefix << "Completed " << i->first); + i->second->finishCompleter(); + } } void QueueGuard::cancel() { queue.removeObserver(observer); + Delayed removed; { Mutex::ScopedLock l(lock); if (delayed.empty()) return; // No need if no delayed messages. + delayed.swap(removed); } - // FIXME aconway 2012-06-15: optimize, only messages in delayed set. - queue.eachMessage(boost::bind(&QueueGuard::complete, this, _1)); + completeRange(removed.begin(), removed.end()); } void QueueGuard::attach(ReplicatingSubscription& rs) { @@ -104,36 +112,39 @@ void QueueGuard::attach(ReplicatingSubscription& rs) { subscription = &rs; } -namespace { -void completeBefore(QueueGuard* guard, SequenceNumber position, const QueuedMessage& qm) { - if (qm.position <= position) guard->complete(qm); -} -} - bool QueueGuard::subscriptionStart(SequenceNumber position) { - // Complete any messages before or at the ReplicatingSubscription start position. - // Those messages are already on the backup. - if (!delayed.empty() && delayed.front() <= position) { - // FIXME aconway 2012-06-15: queue iteration, only messages in delayed - queue.eachMessage(boost::bind(&completeBefore, this, position, _1)); + Delayed removed; + { + Mutex::ScopedLock l(lock); + // Complete any messages before or at the ReplicatingSubscription start position. + // Those messages are already on the backup. + for (Delayed::iterator i = delayed.begin(); i != delayed.end() && i->first <= position;) { + removed.insert(*i); + delayed.erase(i++); + } } + completeRange(removed.begin(), removed.end()); return position >= range.back; } -void QueueGuard::complete(const QueuedMessage& qm) { - assert(qm.queue == &queue); +void QueueGuard::complete(SequenceNumber sequence) { + boost::intrusive_ptr<broker::AsyncCompletion> m; { Mutex::ScopedLock l(lock); // The same message can be completed twice, by // ReplicatingSubscription::acknowledged and dequeued. Remove it - // from the set so we only call finishCompleter() once - if (delayed.contains(qm.position)) - delayed -= qm.position; - else - return; + // from the map so we only call finishCompleter() once + Delayed::iterator i = delayed.find(sequence); + if (i != delayed.end()) { + m = i->second; + delayed.erase(i); + } + + } + if (m) { + QPID_LOG(trace, logPrefix << "Completed " << sequence); + m->finishCompleter(); } - QPID_LOG(trace, logPrefix << "Completed " << qm); - qm.payload->getIngressCompletion().finishCompleter(); } }} // namespaces qpid::ha diff --git a/cpp/src/qpid/ha/QueueGuard.h b/cpp/src/qpid/ha/QueueGuard.h index bc8f40b65f..3904b3bd3f 100644 --- a/cpp/src/qpid/ha/QueueGuard.h +++ b/cpp/src/qpid/ha/QueueGuard.h @@ -63,15 +63,15 @@ class QueueGuard { /** QueueObserver override. Delay completion of the message. * NOTE: Called under the queues message lock. */ - void enqueued(const broker::QueuedMessage&); + void enqueued(const broker::Message&); /** QueueObserver override: Complete a delayed message. * NOTE: Called under the queues message lock. */ - void dequeued(const broker::QueuedMessage&); + void dequeued(const broker::Message&); /** Complete a delayed message. */ - void complete(const broker::QueuedMessage&); + void complete(framing::SequenceNumber); /** Complete all delayed messages. */ void cancel(); @@ -108,10 +108,13 @@ class QueueGuard { sys::Mutex lock; std::string logPrefix; broker::Queue& queue; - framing::SequenceSet delayed; + typedef std::map<framing::SequenceNumber, boost::intrusive_ptr<broker::AsyncCompletion> > Delayed; + Delayed delayed; ReplicatingSubscription* subscription; boost::shared_ptr<QueueObserver> observer; QueueRange range; + + void completeRange(Delayed::iterator begin, Delayed::iterator end); }; }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/QueueReplicator.cpp b/cpp/src/qpid/ha/QueueReplicator.cpp index be910a087f..ae53f89404 100644 --- a/cpp/src/qpid/ha/QueueReplicator.cpp +++ b/cpp/src/qpid/ha/QueueReplicator.cpp @@ -120,8 +120,10 @@ void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHa settings.setTable(ReplicatingSubscription::QPID_BROKER_INFO, brokerInfo.asFieldTable()); SequenceNumber front; - if (ReplicatingSubscription::getFront(*queue, front)) + if (ReplicatingSubscription::getFront(*queue, front)) { settings.setInt(ReplicatingSubscription::QPID_FRONT, front); + QPID_LOG(debug, "QPID_FRONT for " << queue->getName() << " is " << front); + } peer.getMessage().subscribe( args.i_src, args.i_dest, 0/*accept-explicit*/, 1/*not-acquired*/, false/*exclusive*/, "", 0, settings); @@ -137,8 +139,7 @@ void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHa namespace { template <class T> T decodeContent(Message& m) { - std::string content; - m.getFrames().getContent(content); + std::string content = m.getContent(); Buffer buffer(const_cast<char*>(content.c_str()), content.size()); T result; result.decode(buffer); @@ -148,9 +149,7 @@ template <class T> T decodeContent(Message& m) { void QueueReplicator::dequeue(SequenceNumber n, sys::Mutex::ScopedLock&) { // Thread safe: only calls thread safe Queue functions. - QueuedMessage message; - if (queue->acquireMessageAt(n, message)) - queue->dequeue(0, message); + queue->dequeueMessageAt(n); } // Called in connection thread of the queues bridge to primary. diff --git a/cpp/src/qpid/ha/RemoteBackup.cpp b/cpp/src/qpid/ha/RemoteBackup.cpp index a5693fd14e..3421380940 100644 --- a/cpp/src/qpid/ha/RemoteBackup.cpp +++ b/cpp/src/qpid/ha/RemoteBackup.cpp @@ -23,6 +23,7 @@ #include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" #include "qpid/broker/QueueRegistry.h" +#include "qpid/log/Statement.h" #include <boost/bind.hpp> namespace qpid { @@ -32,7 +33,7 @@ using sys::Mutex; using boost::bind; RemoteBackup::RemoteBackup(const BrokerInfo& info, ReplicationTest rt, bool con) : - logPrefix("Primary remote backup "+info.getLogId()+": "), + logPrefix("Primary: Remote backup "+info.getLogId()+": "), brokerInfo(info), replicationTest(rt), connected(con), reportedReady(false) {} diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.cpp b/cpp/src/qpid/ha/ReplicatingSubscription.cpp index c960758eaf..6f7519cd1f 100644 --- a/cpp/src/qpid/ha/ReplicatingSubscription.cpp +++ b/cpp/src/qpid/ha/ReplicatingSubscription.cpp @@ -27,6 +27,7 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/SessionContext.h" #include "qpid/broker/ConnectionState.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/log/Statement.h" @@ -66,10 +67,10 @@ class DequeueScanner at = front - 1; } - void operator()(const QueuedMessage& qm) { - if (qm.position >= front && qm.position <= back) { - if (qm.position > at+1) subscription.dequeued(at+1, qm.position-1); - at = qm.position; + void operator()(const Message& m) { + if (m.getSequence() >= front && m.getSequence() <= back) { + if (m.getSequence() > at+1) subscription.dequeued(at+1, m.getSequence()-1); + at = m.getSequence(); } } @@ -90,37 +91,23 @@ string mask(const string& in) return DOLLAR + in + INTERNAL; } - -/** Dummy consumer used to get the front position on the queue */ -class GetPositionConsumer : public Consumer +namespace { +bool getSequence(const Message& message, SequenceNumber& result) { - public: - GetPositionConsumer() : - Consumer("ha.GetPositionConsumer."+types::Uuid(true).str(), false) {} - bool deliver(broker::QueuedMessage& ) { return true; } - void notify() {} - bool filter(boost::intrusive_ptr<broker::Message>) { return true; } - bool accept(boost::intrusive_ptr<broker::Message>) { return true; } - void cancel() {} - void acknowledged(const broker::QueuedMessage&) {} - bool browseAcquired() const { return true; } - broker::OwnershipToken* getSession() { return 0; } -}; - - + result = message.getSequence(); + return true; +} +} bool ReplicatingSubscription::getNext( broker::Queue& q, SequenceNumber from, SequenceNumber& result) { - boost::shared_ptr<Consumer> c(new GetPositionConsumer); - c->setPosition(from); - if (!q.dispatch(c)) return false; - result = c->getPosition(); - return true; + QueueCursor cursor(REPLICATOR); + return q.seek(cursor, boost::bind(&getSequence, _1, boost::ref(result)), from); } bool ReplicatingSubscription::getFront(broker::Queue& q, SequenceNumber& front) { - // FIXME aconway 2012-05-23: won't wrap, assumes 0 is < all messages in queue. - return getNext(q, 0, front); + QueueCursor cursor(REPLICATOR); + return q.seek(cursor, boost::bind(&getSequence, _1, boost::ref(front))); } /* Called by SemanticState::consume to create a consumer */ @@ -152,15 +139,14 @@ ReplicatingSubscription::ReplicatingSubscription( const string& name, Queue::shared_ptr queue, bool ack, - bool acquire, + bool /*acquire*/, bool exclusive, const string& tag, const string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments -) : ConsumerImpl(parent, name, queue, ack, acquire, exclusive, tag, +) : ConsumerImpl(parent, name, queue, ack, REPLICATOR, exclusive, tag, resumeId, resumeTtl, arguments), - dummy(new Queue(mask(name))), ready(false) { try { @@ -213,6 +199,8 @@ ReplicatingSubscription::ReplicatingSubscription( queue->eachMessage(boost::ref(scan)); // Remove missing messages in between. scan.finish(); position = backup.back; + //move cursor to position + queue->seek(*this, position); } // NOTE: we are assuming that the messages that are on the backup are // consistent with those on the primary. If the backup is a replica @@ -260,32 +248,31 @@ void ReplicatingSubscription::initialize() { } // Message is delivered in the subscription's connection thread. -bool ReplicatingSubscription::deliver(QueuedMessage& qm) { +bool ReplicatingSubscription::deliver(const qpid::broker::QueueCursor& c, const qpid::broker::Message& m) { + position = m.getSequence(); try { - // Add position events for the subscribed queue, not the internal event queue. - if (qm.queue == getQueue().get()) { - QPID_LOG(trace, logPrefix << "Replicating " << qm); - { - Mutex::ScopedLock l(lock); - assert(position == qm.position); - // qm.position is the position of the newly enqueued qm on local queue. - // backupPosition is latest position on backup queue before enqueueing - if (qm.position <= backupPosition) - throw Exception( - QPID_MSG("Expected position > " << backupPosition - << " but got " << qm.position)); - if (qm.position - backupPosition > 1) { - // Position has advanced because of messages dequeued ahead of us. - // Send the position before qm was enqueued. - sendPositionEvent(qm.position-1, l); - } - // Backup will automatically advance by 1 on delivery of message. - backupPosition = qm.position; + QPID_LOG(trace, logPrefix << "Replicating " << getQueue()->getName() << "[" << m.getSequence() << "]"); + { + Mutex::ScopedLock l(lock); + //FIXME GRS: position is no longer set//assert(position == m.getSequence()); + + // m.getSequence() is the position of the newly enqueued message on local queue. + // backupPosition is latest position on backup queue before enqueueing + if (m.getSequence() <= backupPosition) + throw Exception( + QPID_MSG("Expected position > " << backupPosition + << " but got " << m.getSequence())); + if (m.getSequence() - backupPosition > 1) { + // Position has advanced because of messages dequeued ahead of us. + // Send the position before message was enqueued. + sendPositionEvent(m.getSequence()-1, l); } + // Backup will automatically advance by 1 on delivery of message. + backupPosition = m.getSequence(); } - return ConsumerImpl::deliver(qm); + return ConsumerImpl::deliver(c, m); } catch (const std::exception& e) { - QPID_LOG(critical, logPrefix << "Error replicating " << qm + QPID_LOG(critical, logPrefix << "Error replicating " << getQueue()->getName() << "[" << m.getSequence() << "]" << ": " << e.what()); throw; } @@ -310,15 +297,13 @@ void ReplicatingSubscription::cancel() } // Consumer override, called on primary in the backup's IO thread. -void ReplicatingSubscription::acknowledged(const QueuedMessage& qm) { - if (qm.queue == getQueue().get()) { // Don't complete messages on the internal queue - // Finish completion of message, it has been acknowledged by the backup. - QPID_LOG(trace, logPrefix << "Acknowledged " << qm); - guard->complete(qm); - // If next message is protected by the guard then we are ready - if (qm.position >= guard->getRange().back) setReady(); - } - ConsumerImpl::acknowledged(qm); +void ReplicatingSubscription::acknowledged(const broker::DeliveryRecord& r) { + // Finish completion of message, it has been acknowledged by the backup. + QPID_LOG(trace, logPrefix << "Acknowledged " << getQueue()->getName() << "[" << r.getMessageId() << "]"); + guard->complete(r.getMessageId()); + // If next message is protected by the guard then we are ready + if (r.getMessageId() >= guard->getRange().back) setReady(); + ConsumerImpl::acknowledged(r); } // Called with lock held. Called in subscription's connection thread. @@ -341,13 +326,12 @@ void ReplicatingSubscription::sendDequeueEvent(Mutex::ScopedLock&) // Called after the message has been removed // from the deque and under the messageLock in the queue. Called in // arbitrary connection threads. -void ReplicatingSubscription::dequeued(const QueuedMessage& qm) +void ReplicatingSubscription::dequeued(const Message& m) { - assert (qm.queue == getQueue().get()); - QPID_LOG(trace, logPrefix << "Dequeued " << qm); + QPID_LOG(trace, logPrefix << "Dequeued " << getQueue()->getName() << "[" << m.getSequence() << "]"); { Mutex::ScopedLock l(lock); - dequeues.add(qm.position); + dequeues.add(m.getSequence()); } notify(); // Ensure a call to doDispatch } @@ -379,7 +363,7 @@ void ReplicatingSubscription::sendPositionEvent(SequenceNumber pos, Mutex::Scope void ReplicatingSubscription::sendEvent(const std::string& key, framing::Buffer& buffer) { //generate event message - boost::intrusive_ptr<Message> event = new Message(); + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> event(new qpid::broker::amqp_0_10::MessageTransfer()); AMQFrame method((MessageTransferBody(ProtocolVersion(), string(), 0, 0))); AMQFrame header((AMQHeaderBody())); AMQFrame content((AMQContentBody())); @@ -400,10 +384,8 @@ void ReplicatingSubscription::sendEvent(const std::string& key, framing::Buffer& event->getFrames().getHeaders()->get<DeliveryProperties>(true); props->setRoutingKey(key); // Send the event directly to the base consumer implementation. - // We don't really need a queue here but we pass a dummy queue - // to conform to the consumer API. - QueuedMessage qm(dummy.get(), event); - ConsumerImpl::deliver(qm); + //dummy consumer prevents acknowledgements being handled, which is what we want for events + ConsumerImpl::deliver(QueueCursor(), Message(event, 0), boost::shared_ptr<Consumer>()); } diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.h b/cpp/src/qpid/ha/ReplicatingSubscription.h index a80141a6c2..8a2984846e 100644 --- a/cpp/src/qpid/ha/ReplicatingSubscription.h +++ b/cpp/src/qpid/ha/ReplicatingSubscription.h @@ -101,15 +101,15 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl // Called via QueueGuard::dequeued. //@return true if the message requires completion. - void dequeued(const broker::QueuedMessage& qm); + void dequeued(const broker::Message&); // Called during initial scan for dequeues. void dequeued(framing::SequenceNumber first, framing::SequenceNumber last); // Consumer overrides. - bool deliver(broker::QueuedMessage& msg); + bool deliver(const broker::QueueCursor& cursor, const broker::Message& msg); void cancel(); - void acknowledged(const broker::QueuedMessage&); + void acknowledged(const broker::DeliveryRecord&); bool browseAcquired() const { return true; } // Hide the "queue deleted" error for a ReplicatingSubscription when a // queue is deleted, this is normal and not an error. @@ -127,8 +127,8 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl private: std::string logPrefix; - boost::shared_ptr<broker::Queue> dummy; // Used to send event messages framing::SequenceSet dequeues; + framing::SequenceNumber position; framing::SequenceNumber backupPosition; bool ready; BrokerInfo info; diff --git a/cpp/src/qpid/ha/ReplicationTest.cpp b/cpp/src/qpid/ha/ReplicationTest.cpp index 18e0953930..88a969dbfd 100644 --- a/cpp/src/qpid/ha/ReplicationTest.cpp +++ b/cpp/src/qpid/ha/ReplicationTest.cpp @@ -68,7 +68,7 @@ bool ReplicationTest::isReplicated( bool ReplicationTest::isReplicated(ReplicateLevel level, const broker::Queue& q) { - return isReplicated(level, q.getSettings(), q.isAutoDelete(), q.hasExclusiveOwner()); + return isReplicated(level, q.getSettings().storeSettings, q.isAutoDelete(), q.hasExclusiveOwner()); } diff --git a/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp index 7d90ed99d0..474c86ed48 100644 --- a/cpp/src/qpid/management/ManagementAgent.cpp +++ b/cpp/src/qpid/management/ManagementAgent.cpp @@ -31,6 +31,7 @@ #include <qpid/broker/Message.h> #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/FieldValue.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/sys/Time.h" #include "qpid/sys/Thread.h" #include "qpid/broker/ConnectionState.h" @@ -535,7 +536,7 @@ void ManagementAgent::sendBufferLH(Buffer& buf, } if (exchange.get() == 0) return; - intrusive_ptr<Message> msg(new Message()); + intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer()); AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange->getName (), 0, 0))); AMQFrame header((AMQHeaderBody())); AMQFrame content((AMQContentBody())); @@ -547,24 +548,26 @@ void ManagementAgent::sendBufferLH(Buffer& buf, header.setEof(false); content.setBof(false); - msg->getFrames().append(method); - msg->getFrames().append(header); + transfer->getFrames().append(method); + transfer->getFrames().append(header); MessageProperties* props = - msg->getFrames().getHeaders()->get<MessageProperties>(true); + transfer->getFrames().getHeaders()->get<MessageProperties>(true); props->setContentLength(length); DeliveryProperties* dp = - msg->getFrames().getHeaders()->get<DeliveryProperties>(true); + transfer->getFrames().getHeaders()->get<DeliveryProperties>(true); dp->setRoutingKey(routingKey); - msg->getFrames().append(content); - msg->setIsManagementMessage(true); + transfer->getFrames().append(content); + + Message msg(transfer, transfer); + msg.setIsManagementMessage(true); { sys::Mutex::ScopedUnlock u(userLock); - DeliverableMessage deliverable (msg); + DeliverableMessage deliverable (msg, 0); try { exchange->route(deliverable); } catch(exception&) {} @@ -602,7 +605,7 @@ void ManagementAgent::sendBufferLH(const string& data, } if (exchange.get() == 0) return; - intrusive_ptr<Message> msg(new Message()); + intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer); AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange->getName (), 0, 0))); AMQFrame header((AMQHeaderBody())); AMQFrame content((AMQContentBody(data))); @@ -612,11 +615,11 @@ void ManagementAgent::sendBufferLH(const string& data, header.setEof(false); content.setBof(false); - msg->getFrames().append(method); - msg->getFrames().append(header); + transfer->getFrames().append(method); + transfer->getFrames().append(header); MessageProperties* props = - msg->getFrames().getHeaders()->get<MessageProperties>(true); + transfer->getFrames().getHeaders()->get<MessageProperties>(true); props->setContentLength(data.length()); if (!cid.empty()) { props->setCorrelationId(cid); @@ -625,23 +628,25 @@ void ManagementAgent::sendBufferLH(const string& data, props->setAppId("qmf2"); for (i = headers.begin(); i != headers.end(); ++i) { - msg->insertCustomProperty(i->first, i->second.asString()); + props->getApplicationHeaders().setString(i->first, i->second.asString()); } DeliveryProperties* dp = - msg->getFrames().getHeaders()->get<DeliveryProperties>(true); + transfer->getFrames().getHeaders()->get<DeliveryProperties>(true); dp->setRoutingKey(routingKey); if (ttl_msec) { dp->setTtl(ttl_msec); - msg->computeExpiration(broker->getExpiryPolicy()); } - msg->getFrames().append(content); - msg->setIsManagementMessage(true); + transfer->getFrames().append(content); + transfer->computeRequiredCredit(); + Message msg(transfer, transfer); + msg.setIsManagementMessage(true); + msg.computeExpiration(broker->getExpiryPolicy()); { sys::Mutex::ScopedUnlock u(userLock); - DeliverableMessage deliverable (msg); + DeliverableMessage deliverable (msg, 0); try { exchange->route(deliverable); } catch(exception&) {} @@ -2135,19 +2140,20 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg) // authorized or not. In this case, return true (authorized) if there is no ACL in place, // otherwise return false; // - if (msg.encodedSize() > MA_BUFFER_SIZE) + if (msg.getContentSize() > MA_BUFFER_SIZE) return broker->getAcl() == 0; - msg.encodeContent(inBuffer); + inBuffer.putRawData(msg.getContent()); uint32_t bufferLen = inBuffer.getPosition(); inBuffer.reset(); + qpid::broker::amqp_0_10::MessageTransfer& transfer(qpid::broker::amqp_0_10::MessageTransfer::get(msg)); const framing::MessageProperties* p = - msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + transfer.getFrames().getHeaders()->get<framing::MessageProperties>(); - const framing::FieldTable *headers = msg.getApplicationHeaders(); + const framing::FieldTable *headers = p ? &p->getApplicationHeaders() : 0; - if (headers && msg.getAppId() == "qmf2") + if (headers && p->getAppId() == "qmf2") { mapMsg = true; @@ -2238,8 +2244,9 @@ bool ManagementAgent::authorizeAgentMessageLH(Message& msg) // authorization failed, send reply if replyTo present + qpid::broker::amqp_0_10::MessageTransfer& transfer(qpid::broker::amqp_0_10::MessageTransfer::get(msg)); const framing::MessageProperties* p = - msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + transfer.getFrames().getHeaders()->get<framing::MessageProperties>(); if (p && p->hasReplyTo()) { const framing::ReplyTo& rt = p->getReplyTo(); string rte = rt.getExchange(); @@ -2277,8 +2284,9 @@ void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal) { string rte; string rtk; + qpid::broker::amqp_0_10::MessageTransfer& transfer(qpid::broker::amqp_0_10::MessageTransfer::get(msg)); const framing::MessageProperties* p = - msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + transfer.getFrames().getHeaders()->get<framing::MessageProperties>(); if (p && p->hasReplyTo()) { const framing::ReplyTo& rt = p->getReplyTo(); rte = rt.getExchange(); @@ -2290,19 +2298,19 @@ void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal) Buffer inBuffer(inputBuffer, MA_BUFFER_SIZE); uint8_t opcode; - if (msg.encodedSize() > MA_BUFFER_SIZE) { + if (msg.getContentSize() > MA_BUFFER_SIZE) { QPID_LOG(debug, "ManagementAgent::dispatchAgentCommandLH: Message too large: " << - msg.encodedSize()); + msg.getContentSize()); return; } - msg.encodeContent(inBuffer); + inBuffer.putRawData(msg.getContent()); uint32_t bufferLen = inBuffer.getPosition(); inBuffer.reset(); ScopedManagementContext context((const qpid::broker::ConnectionState*) msg.getPublisher()); - const framing::FieldTable *headers = msg.getApplicationHeaders(); - if (headers && msg.getAppId() == "qmf2") + const framing::FieldTable *headers = p ? &p->getApplicationHeaders() : 0; + if (headers && p->getAppId() == "qmf2") { string opcode = headers->getAsString("qmf.opcode"); string contentType = headers->getAsString("qmf.content"); diff --git a/cpp/src/qpid/management/ManagementDirectExchange.cpp b/cpp/src/qpid/management/ManagementDirectExchange.cpp index 9432a21b3a..1c1d6ef3db 100644 --- a/cpp/src/qpid/management/ManagementDirectExchange.cpp +++ b/cpp/src/qpid/management/ManagementDirectExchange.cpp @@ -43,11 +43,9 @@ ManagementDirectExchange::ManagementDirectExchange(const std::string& _name, void ManagementDirectExchange::route(Deliverable& msg) { bool routeIt = true; - const std::string& routingKey = msg.getMessage().getRoutingKey(); - const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (managementAgent) - routeIt = managementAgent->dispatchCommand(msg, routingKey, args, false, qmfVersion); + routeIt = managementAgent->dispatchCommand(msg, msg.getMessage().getRoutingKey(), 0/*args - TODO*/, false, qmfVersion); if (routeIt) DirectExchange::route(msg); diff --git a/cpp/src/qpid/management/ManagementTopicExchange.cpp b/cpp/src/qpid/management/ManagementTopicExchange.cpp index e5b659f217..c8bfef3785 100644 --- a/cpp/src/qpid/management/ManagementTopicExchange.cpp +++ b/cpp/src/qpid/management/ManagementTopicExchange.cpp @@ -42,12 +42,10 @@ ManagementTopicExchange::ManagementTopicExchange(const std::string& _name, void ManagementTopicExchange::route(Deliverable& msg) { bool routeIt = true; - const std::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); + routeIt = managementAgent->dispatchCommand(msg, msg.getMessage().getRoutingKey(), 0/*args - TODO*/, true, qmfVersion); if (routeIt) TopicExchange::route(msg); diff --git a/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/cpp/src/qpid/replication/ReplicatingEventListener.cpp deleted file mode 100644 index 9284bda388..0000000000 --- a/cpp/src/qpid/replication/ReplicatingEventListener.cpp +++ /dev/null @@ -1,200 +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/replication/ReplicatingEventListener.h" -#include "qpid/replication/constants.h" -#include "qpid/broker/Broker.h" -#include "qpid/broker/DeliverableMessage.h" -#include "qpid/broker/QueueEvents.h" -#include "qpid/framing/AMQFrame.h" -#include "qpid/framing/FrameHandler.h" -#include "qpid/framing/MessageTransferBody.h" -#include "qpid/log/Statement.h" - -namespace qpid { -namespace replication { - -using namespace qpid::broker; -using namespace qpid::framing; -using namespace qpid::replication::constants; - -void ReplicatingEventListener::handle(QueueEvents::Event event) -{ - switch (event.type) { - case QueueEvents::ENQUEUE: - deliverEnqueueMessage(event.msg); - QPID_LOG(debug, "Queuing 'enqueue' event on " << event.msg.queue->getName() << " for replication"); - break; - case QueueEvents::DEQUEUE: - deliverDequeueMessage(event.msg); - QPID_LOG(debug, "Queuing 'dequeue' event from " << event.msg.queue->getName() << " for replication, (from position " - << event.msg.position << ")"); - break; - } -} - -namespace { -const std::string EMPTY; -} - -void ReplicatingEventListener::deliverDequeueMessage(const QueuedMessage& dequeued) -{ - FieldTable headers; - headers.setString(REPLICATION_TARGET_QUEUE, dequeued.queue->getName()); - headers.setInt(REPLICATION_EVENT_TYPE, DEQUEUE); - headers.setInt(DEQUEUED_MESSAGE_POSITION, dequeued.position); - boost::intrusive_ptr<Message> msg(createMessage(headers)); - DeliveryProperties* props = msg->getFrames().getHeaders()->get<DeliveryProperties>(true); - props->setRoutingKey(dequeued.queue->getName()); - route(msg); -} - -void ReplicatingEventListener::deliverEnqueueMessage(const QueuedMessage& enqueued) -{ - boost::intrusive_ptr<Message> msg(cloneMessage(*(enqueued.queue), enqueued.payload)); - msg->insertCustomProperty(REPLICATION_TARGET_QUEUE, enqueued.queue->getName()); - msg->insertCustomProperty(REPLICATION_EVENT_TYPE, ENQUEUE); - msg->insertCustomProperty(QUEUE_MESSAGE_POSITION,enqueued.position); - route(msg); -} - -void ReplicatingEventListener::route(boost::intrusive_ptr<qpid::broker::Message> msg) -{ - try { - if (exchange) { - DeliverableMessage deliverable(msg); - exchange->route(deliverable); - } else if (queue) { - queue->deliver(msg); - } else { - QPID_LOG(error, "Cannot route replication event, neither replication queue nor exchange configured"); - } - } catch (const std::exception& e) { - QPID_LOG(error, "Error enqueing replication event: " << e.what()); - } -} - - -boost::intrusive_ptr<Message> ReplicatingEventListener::createMessage(const FieldTable& headers) -{ - boost::intrusive_ptr<Message> msg(new Message()); - AMQFrame method((MessageTransferBody(ProtocolVersion(), EMPTY, 0, 0))); - AMQFrame header((AMQHeaderBody())); - header.setBof(false); - header.setEof(true); - header.setBos(true); - header.setEos(true); - msg->getFrames().append(method); - msg->getFrames().append(header); - MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true); - props->setApplicationHeaders(headers); - return msg; -} - -struct AppendingHandler : FrameHandler -{ - boost::intrusive_ptr<Message> msg; - - AppendingHandler(boost::intrusive_ptr<Message> m) : msg(m) {} - - void handle(AMQFrame& f) - { - msg->getFrames().append(f); - } -}; - -boost::intrusive_ptr<Message> ReplicatingEventListener::cloneMessage(Queue& queue, boost::intrusive_ptr<Message> original) -{ - boost::intrusive_ptr<Message> copy(new Message()); - AMQFrame method((MessageTransferBody(ProtocolVersion(), EMPTY, 0, 0))); - AppendingHandler handler(copy); - handler.handle(method); - - //To avoid modifying original headers, create new frame with - //cloned body: - AMQFrame header(*original->getFrames().getHeaders()); - header.setBof(false); - 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); - - original->sendContent(queue, handler, std::numeric_limits<int16_t>::max()); - return copy; -} - -Options* ReplicatingEventListener::getOptions() -{ - return &options; -} - -void ReplicatingEventListener::initialize(Plugin::Target& target) -{ - Broker* broker = dynamic_cast<broker::Broker*>(&target); - if (broker) { - broker->addFinalizer(boost::bind(&ReplicatingEventListener::shutdown, this)); - if (!options.exchange.empty()) { - if (!options.queue.empty()) { - QPID_LOG(warning, "Replication queue option ignored as replication exchange has been specified"); - } - try { - exchange = broker->getExchanges().declare(options.exchange, options.exchangeType).first; - } catch (const UnknownExchangeTypeException&) { - QPID_LOG(error, "Replication disabled due to invalid type: " << options.exchangeType); - } - } else if (!options.queue.empty()) { - if (options.createQueue) { - queue = broker->getQueues().declare(options.queue).first; - } else { - queue = broker->getQueues().find(options.queue); - } - if (queue) { - queue->insertSequenceNumbers(REPLICATION_EVENT_SEQNO); - } else { - QPID_LOG(error, "Replication queue named '" << options.queue << "' does not exist; replication plugin disabled."); - } - } - if (queue || exchange) { - QueueEvents::EventListener callback = boost::bind(&ReplicatingEventListener::handle, this, _1); - broker->getQueueEvents().registerListener(options.name, callback); - QPID_LOG(info, "Registered replicating queue event listener"); - } - } -} - -void ReplicatingEventListener::earlyInitialize(Target&) {} -void ReplicatingEventListener::shutdown() { queue.reset(); exchange.reset(); } - -ReplicatingEventListener::PluginOptions::PluginOptions() : Options("Queue Replication Options"), - exchangeType("direct"), - name("replicator"), - createQueue(false) -{ - addOptions() - ("replication-exchange-name", optValue(exchange, "EXCHANGE"), "Exchange to which events for other queues are routed") - ("replication-exchange-type", optValue(exchangeType, "direct|topic etc"), "Type of exchange to use") - ("replication-queue", optValue(queue, "QUEUE"), "Queue on which events for other queues are recorded") - ("replication-listener-name", optValue(name, "NAME"), "name by which to register the replicating event listener") - ("create-replication-queue", optValue(createQueue), "if set, the replication will be created if it does not exist"); -} - -static ReplicatingEventListener plugin; - -}} // namespace qpid::replication diff --git a/cpp/src/qpid/replication/ReplicatingEventListener.h b/cpp/src/qpid/replication/ReplicatingEventListener.h deleted file mode 100644 index 74418d00e6..0000000000 --- a/cpp/src/qpid/replication/ReplicatingEventListener.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef QPID_REPLICATION_REPLICATINGEVENTLISTENER_H -#define QPID_REPLICATION_REPLICATINGEVENTLISTENER_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/Plugin.h" -#include "qpid/Options.h" -#include "qpid/broker/Exchange.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/QueueEvents.h" -#include "qpid/framing/FieldTable.h" -#include "qpid/framing/SequenceNumber.h" - -namespace qpid { -namespace replication { - -/** - * An event listener plugin that records queue events as messages on a - * replication queue, from where they can be consumed (e.g. by an - * inter-broker link to the corresponding QueueReplicationExchange - * plugin. - */ -class ReplicatingEventListener : public Plugin -{ - public: - Options* getOptions(); - void earlyInitialize(Plugin::Target& target); - void initialize(Plugin::Target& target); - void handle(qpid::broker::QueueEvents::Event); - private: - struct PluginOptions : public Options - { - std::string queue; - std::string exchange; - std::string exchangeType; - std::string name; - bool createQueue; - - PluginOptions(); - }; - - PluginOptions options; - qpid::broker::Queue::shared_ptr queue; - qpid::broker::Exchange::shared_ptr exchange; - - void deliverDequeueMessage(const qpid::broker::QueuedMessage& enqueued); - void deliverEnqueueMessage(const qpid::broker::QueuedMessage& enqueued); - void route(boost::intrusive_ptr<qpid::broker::Message>); - void shutdown(); - - boost::intrusive_ptr<qpid::broker::Message> createMessage(const qpid::framing::FieldTable& headers); - boost::intrusive_ptr<qpid::broker::Message> cloneMessage(qpid::broker::Queue& queue, - boost::intrusive_ptr<qpid::broker::Message> original); -}; - -}} // namespace qpid::replication - -#endif /*!QPID_REPLICATION_REPLICATINGEVENTLISTENER_H*/ diff --git a/cpp/src/qpid/replication/ReplicationExchange.cpp b/cpp/src/qpid/replication/ReplicationExchange.cpp deleted file mode 100644 index bcb7c7f293..0000000000 --- a/cpp/src/qpid/replication/ReplicationExchange.cpp +++ /dev/null @@ -1,240 +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/replication/ReplicationExchange.h" -#include "qpid/replication/constants.h" -#include "qpid/Plugin.h" -#include "qpid/broker/Broker.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/QueueRegistry.h" -#include "qpid/broker/ExchangeRegistry.h" -#include "qpid/framing/reply_exceptions.h" -#include "qpid/log/Statement.h" -#include <boost/bind.hpp> - -namespace qpid { -namespace replication { - -using namespace qpid::broker; -using namespace qpid::framing; -using namespace qpid::replication::constants; - -const std::string SEQUENCE_VALUE("qpid.replication-event.sequence"); -ReplicationExchange::ReplicationExchange(const std::string& name, bool durable, - const FieldTable& _args, - QueueRegistry& qr, - Manageable* parent, Broker* broker) - : Exchange(name, durable, _args, parent, broker), queues(qr), sequence(args.getAsInt64(SEQUENCE_VALUE)), init(false) -{ - args.setInt64(SEQUENCE_VALUE, sequence); - if (mgmtExchange != 0) - mgmtExchange->set_type(typeName); -} - -std::string ReplicationExchange::getType() const { return typeName; } - -void ReplicationExchange::route(Deliverable& msg) -{ - const FieldTable* args = msg.getMessage().getApplicationHeaders(); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives(); - mgmtExchange->inc_byteReceives(msg.contentSize()); - } - if (args) { - int eventType = args->getAsInt(REPLICATION_EVENT_TYPE); - if (eventType) { - if (isDuplicate(args)) { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } - return; - } - switch (eventType) { - case ENQUEUE: - handleEnqueueEvent(args, msg); - return; - case DEQUEUE: - handleDequeueEvent(args, msg); - return; - default: - throw IllegalArgumentException(QPID_MSG("Illegal value for " << REPLICATION_EVENT_TYPE << ": " << eventType)); - } - } - } else { - QPID_LOG(warning, "Dropping unexpected message with no headers"); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } - } -} - -void ReplicationExchange::handleEnqueueEvent(const FieldTable* args, Deliverable& msg) -{ - std::string queueName = args->getAsString(REPLICATION_TARGET_QUEUE); - Queue::shared_ptr queue = queues.find(queueName); - if (queue) { - - SequenceNumber seqno1(args->getAsInt(QUEUE_MESSAGE_POSITION)); - - // note that queue will ++ before enqueue. - if (queue->getPosition() > --seqno1) // test queue.pos < seqnumber - { - QPID_LOG(error, "Cannot enqueue replicated message. Destination Queue " << queueName << " ahead of source queue"); - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } else { - queue->setPosition(seqno1); - - msg.getMessage().removeCustomProperty(REPLICATION_TARGET_QUEUE); - msg.getMessage().removeCustomProperty(REPLICATION_EVENT_SEQNO); - msg.getMessage().removeCustomProperty(REPLICATION_EVENT_TYPE); - msg.getMessage().removeCustomProperty(QUEUE_MESSAGE_POSITION); - msg.deliverTo(queue); - QPID_LOG(debug, "Enqueued replicated message onto " << queueName); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes(); - mgmtExchange->inc_byteRoutes( msg.contentSize()); - } - } - } else { - QPID_LOG(error, "Cannot enqueue replicated message. Queue " << queueName << " does not exist"); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } - } -} - -void ReplicationExchange::handleDequeueEvent(const FieldTable* args, Deliverable& msg) -{ - std::string queueName = args->getAsString(REPLICATION_TARGET_QUEUE); - Queue::shared_ptr queue = queues.find(queueName); - if (queue) { - SequenceNumber position(args->getAsInt(DEQUEUED_MESSAGE_POSITION)); - QueuedMessage dequeued; - if (queue->acquireMessageAt(position, dequeued)) { - queue->dequeue(0, dequeued); - QPID_LOG(debug, "Processed replicated 'dequeue' event from " << queueName << " at position " << position); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes(); - mgmtExchange->inc_byteRoutes(msg.contentSize()); - } - } else { - QPID_LOG(warning, "Could not acquire message " << position << " from " << queueName); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } - } - } else { - QPID_LOG(error, "Cannot process replicated 'dequeue' event. Queue " << queueName << " does not exist"); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops(); - mgmtExchange->inc_byteDrops(msg.contentSize()); - } - } -} - -bool ReplicationExchange::isDuplicate(const FieldTable* args) -{ - if (!args->get(REPLICATION_EVENT_SEQNO)) return false; - SequenceNumber seqno(args->getAsInt(REPLICATION_EVENT_SEQNO)); - if (!init) { - init = true; - sequence = seqno; - return false; - } else if (seqno > sequence) { - if (seqno - sequence > 1) { - QPID_LOG(error, "Gap in replication event sequence between: " << sequence << " and " << seqno); - } - sequence = seqno; - return false; - } else { - QPID_LOG(info, "Duplicate detected: seqno=" << seqno << " (last seqno=" << sequence << ")"); - return true; - } -} - -bool ReplicationExchange::bind(Queue::shared_ptr /*queue*/, const std::string& /*routingKey*/, const FieldTable* /*args*/) -{ - throw NotImplementedException("Replication exchange does not support bind operation"); -} - -bool ReplicationExchange::unbind(Queue::shared_ptr /*queue*/, const std::string& /*routingKey*/, const FieldTable* /*args*/) -{ - throw NotImplementedException("Replication exchange does not support unbind operation"); -} - -bool ReplicationExchange::isBound(Queue::shared_ptr /*queue*/, const std::string* const /*routingKey*/, const FieldTable* const /*args*/) -{ - return false; -} - -const std::string ReplicationExchange::typeName("replication"); - - -void ReplicationExchange::encode(Buffer& buffer) const -{ - args.setInt64(std::string(SEQUENCE_VALUE), sequence); - Exchange::encode(buffer); -} - - -struct ReplicationExchangePlugin : Plugin -{ - Broker* broker; - - ReplicationExchangePlugin(); - void earlyInitialize(Plugin::Target& target); - void initialize(Plugin::Target& target); - Exchange::shared_ptr create(const std::string& name, bool durable, - const framing::FieldTable& args, - management::Manageable* parent, - qpid::broker::Broker* broker); -}; - -ReplicationExchangePlugin::ReplicationExchangePlugin() : broker(0) {} - -Exchange::shared_ptr ReplicationExchangePlugin::create(const std::string& name, bool durable, - const framing::FieldTable& args, - management::Manageable* parent, qpid::broker::Broker* broker) -{ - Exchange::shared_ptr e(new ReplicationExchange(name, durable, args, broker->getQueues(), parent, broker)); - return e; -} - - -void ReplicationExchangePlugin::earlyInitialize(Plugin::Target& target) -{ - broker = dynamic_cast<broker::Broker*>(&target); - if (broker) { - ExchangeRegistry::FactoryFunction f = boost::bind(&ReplicationExchangePlugin::create, this, _1, _2, _3, _4, _5); - broker->getExchanges().registerType(ReplicationExchange::typeName, f); - QPID_LOG(info, "Registered replication exchange"); - } -} - -void ReplicationExchangePlugin::initialize(Target&) {} - -static ReplicationExchangePlugin exchangePlugin; - -}} // namespace qpid::replication diff --git a/cpp/src/qpid/replication/ReplicationExchange.h b/cpp/src/qpid/replication/ReplicationExchange.h deleted file mode 100644 index ff0a98c48e..0000000000 --- a/cpp/src/qpid/replication/ReplicationExchange.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef QPID_REPLICATION_REPLICATIONEXCHANGE_H -#define QPID_REPLICATION_REPLICATIONEXCHANGE_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/Exchange.h" -#include "qpid/framing/Buffer.h" -#include "qpid/framing/SequenceNumber.h" - -namespace qpid { - -namespace broker { -class QueueRegistry; -} - -namespace replication { - -/** - * A custom exchange plugin that processes incoming messages - * representing enqueue or dequeue events for particular queues and - * carries out the corresponding action to replicate that on the local - * broker. - */ -class ReplicationExchange : public qpid::broker::Exchange -{ - public: - static const std::string typeName; - - ReplicationExchange(const std::string& name, bool durable, - const qpid::framing::FieldTable& args, - qpid::broker::QueueRegistry& queues, - qpid::management::Manageable* parent = 0, - qpid::broker::Broker* broker = 0); - - std::string getType() const; - - 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); - bool isBound(boost::shared_ptr<broker::Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args); - private: - qpid::broker::QueueRegistry& queues; - qpid::framing::SequenceNumber sequence; - bool init; - - bool isDuplicate(const qpid::framing::FieldTable* args); - void handleEnqueueEvent(const qpid::framing::FieldTable* args, qpid::broker::Deliverable& msg); - void handleDequeueEvent(const qpid::framing::FieldTable* args, qpid::broker::Deliverable& msg); - void encode(framing::Buffer& buffer) const; -}; -}} // namespace qpid::replication - -#endif /*!QPID_REPLICATION_REPLICATIONEXCHANGE_H*/ diff --git a/cpp/src/qpid/replication/constants.h b/cpp/src/qpid/replication/constants.h deleted file mode 100644 index c5ba7d3d6a..0000000000 --- a/cpp/src/qpid/replication/constants.h +++ /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. - * - */ -namespace qpid { -namespace replication { -namespace constants { - -const std::string REPLICATION_EVENT_TYPE("qpid.replication.type"); -const std::string REPLICATION_EVENT_SEQNO("qpid.replication.seqno"); -const std::string REPLICATION_TARGET_QUEUE("qpid.replication.target_queue"); -const std::string DEQUEUED_MESSAGE_POSITION("qpid.replication.message"); -const std::string QUEUE_MESSAGE_POSITION("qpid.replication.queue.position"); - -const int ENQUEUE(1); -const int DEQUEUE(2); - -}}} diff --git a/cpp/src/qpid/store/MessageStorePlugin.cpp b/cpp/src/qpid/store/MessageStorePlugin.cpp index 20231bf910..c6b0e1a53a 100644 --- a/cpp/src/qpid/store/MessageStorePlugin.cpp +++ b/cpp/src/qpid/store/MessageStorePlugin.cpp @@ -249,7 +249,7 @@ MessageStorePlugin::destroy(const broker::PersistableConfig& config) void MessageStorePlugin::stage(const boost::intrusive_ptr<broker::PersistableMessage>& msg) { - if (msg->getPersistenceId() == 0 && !msg->isContentReleased()) { + if (msg->getPersistenceId() == 0) { provider->second->stage(msg); } } diff --git a/cpp/src/qpid/sys/AsynchIO.h b/cpp/src/qpid/sys/AsynchIO.h index 41f74f7ed0..b2eaaac9de 100644 --- a/cpp/src/qpid/sys/AsynchIO.h +++ b/cpp/src/qpid/sys/AsynchIO.h @@ -76,8 +76,8 @@ protected: }; struct AsynchIOBufferBase { - char* const bytes; - const int32_t byteCount; + char* bytes; + int32_t byteCount; int32_t dataStart; int32_t dataCount; @@ -134,9 +134,21 @@ public: BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0); public: + /* + * Size of IO buffers - this is the maximum possible frame size + 1 + */ + const static uint32_t MaxBufferSize = 65536; + + /* + * Number of IO buffers allocated - I think the code can only use 2 - + * 1 for reading and 1 for writing, allocate 4 for safety + */ + const static uint32_t BufferCount = 4; + virtual void queueForDeletion() = 0; virtual void start(boost::shared_ptr<Poller> poller) = 0; + virtual void createBuffers(uint32_t size = MaxBufferSize) = 0; virtual void queueReadBuffer(BufferBase* buff) = 0; virtual void unread(BufferBase* buff) = 0; virtual void queueWrite(BufferBase* buff) = 0; diff --git a/cpp/src/qpid/sys/AsynchIOHandler.cpp b/cpp/src/qpid/sys/AsynchIOHandler.cpp index 8a485db72d..2e117a3fb7 100644 --- a/cpp/src/qpid/sys/AsynchIOHandler.cpp +++ b/cpp/src/qpid/sys/AsynchIOHandler.cpp @@ -33,15 +33,6 @@ namespace qpid { namespace sys { -// Buffer definition -struct Buff : public AsynchIO::BufferBase { - Buff() : - AsynchIO::BufferBase(new char[65536], 65536) - {} - ~Buff() - { delete [] bytes;} -}; - struct ProtocolTimeoutTask : public sys::TimerTask { AsynchIOHandler& handler; std::string id; @@ -79,7 +70,7 @@ AsynchIOHandler::~AsynchIOHandler() { delete codec; } -void AsynchIOHandler::init(qpid::sys::AsynchIO* a, qpid::sys::Timer& timer, uint32_t maxTime, int numBuffs) { +void AsynchIOHandler::init(qpid::sys::AsynchIO* a, qpid::sys::Timer& timer, uint32_t maxTime) { aio = a; // Start timer for this connection @@ -87,17 +78,14 @@ void AsynchIOHandler::init(qpid::sys::AsynchIO* a, qpid::sys::Timer& timer, uint timer.add(timeoutTimerTask); // Give connection some buffers to use - for (int i = 0; i < numBuffs; i++) { - aio->queueReadBuffer(new Buff); - } + aio->createBuffers(); } void AsynchIOHandler::write(const framing::ProtocolInitiation& data) { QPID_LOG(debug, "SENT [" << identifier << "]: INIT(" << data << ")"); AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); - if (!buff) - buff = new Buff; + assert(buff); framing::Buffer out(buff->bytes, buff->byteCount); data.encode(out); buff->dataCount = data.encodedSize(); @@ -244,24 +232,24 @@ void AsynchIOHandler::idle(AsynchIO&){ return; } if (codec == 0) return; - try { - if (codec->canEncode()) { - // Try and get a queued buffer if not then construct new one - AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); - if (!buff) buff = new Buff; + if (!codec->canEncode()) { + return; + } + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (buff) { + try { size_t encoded=codec->encode(buff->bytes, buff->byteCount); buff->dataCount = encoded; aio->queueWrite(buff); + if (!codec->isClosed()) { + return; + } + } catch (const std::exception& e) { + QPID_LOG(error, e.what()); } - if (codec->isClosed()) { - readError = true; - aio->queueWriteClose(); - } - } catch (const std::exception& e) { - QPID_LOG(error, e.what()); - readError = true; - aio->queueWriteClose(); } + readError = true; + aio->queueWriteClose(); } }} // namespace qpid::sys diff --git a/cpp/src/qpid/sys/AsynchIOHandler.h b/cpp/src/qpid/sys/AsynchIOHandler.h index 307aad5b85..fd0bc140e5 100644 --- a/cpp/src/qpid/sys/AsynchIOHandler.h +++ b/cpp/src/qpid/sys/AsynchIOHandler.h @@ -61,7 +61,7 @@ class AsynchIOHandler : public OutputControl { public: QPID_COMMON_EXTERN AsynchIOHandler(const std::string& id, qpid::sys::ConnectionCodec::Factory* f ); QPID_COMMON_EXTERN ~AsynchIOHandler(); - QPID_COMMON_EXTERN void init(AsynchIO* a, Timer& timer, uint32_t maxTime, int numBuffs); + QPID_COMMON_EXTERN void init(AsynchIO* a, Timer& timer, uint32_t maxTime); QPID_COMMON_INLINE_EXTERN void setClient() { isClient = true; } diff --git a/cpp/src/qpid/sys/SslPlugin.cpp b/cpp/src/qpid/sys/SslPlugin.cpp index 3b50527c0a..069e97758e 100644 --- a/cpp/src/qpid/sys/SslPlugin.cpp +++ b/cpp/src/qpid/sys/SslPlugin.cpp @@ -191,7 +191,7 @@ void SslEstablished(Poller::shared_ptr poller, const qpid::sys::SslSocket& s, boost::bind(&qpid::sys::ssl::SslHandler::nobuffs, async, _1), boost::bind(&qpid::sys::ssl::SslHandler::idle, async, _1)); - async->init(aio,timer, maxTime, 4); + async->init(aio,timer, maxTime); aio->start(poller); } @@ -247,7 +247,7 @@ void SslMuxProtocolFactory::established(Poller::shared_ptr poller, const Socket& boost::bind(&AsynchIOHandler::nobuffs, async, _1), boost::bind(&AsynchIOHandler::idle, async, _1)); - async->init(aio, brokerTimer, maxNegotiateTime, 4); + async->init(aio, brokerTimer, maxNegotiateTime); aio->start(poller); } diff --git a/cpp/src/qpid/sys/TCPIOPlugin.cpp b/cpp/src/qpid/sys/TCPIOPlugin.cpp index 551440f954..ed7cc3748d 100644 --- a/cpp/src/qpid/sys/TCPIOPlugin.cpp +++ b/cpp/src/qpid/sys/TCPIOPlugin.cpp @@ -166,7 +166,7 @@ void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socke boost::bind(&AsynchIOHandler::nobuffs, async, _1), boost::bind(&AsynchIOHandler::idle, async, _1)); - async->init(aio, brokerTimer, maxNegotiateTime, 4); + async->init(aio, brokerTimer, maxNegotiateTime); aio->start(poller); } diff --git a/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/cpp/src/qpid/sys/epoll/EpollPoller.cpp index dcc9d9181c..c23403c66d 100644 --- a/cpp/src/qpid/sys/epoll/EpollPoller.cpp +++ b/cpp/src/qpid/sys/epoll/EpollPoller.cpp @@ -221,8 +221,8 @@ class PollerPrivate { } }; - static ReadablePipe alwaysReadable; - static int alwaysReadableFd; + ReadablePipe alwaysReadable; + int alwaysReadableFd; class InterruptHandle: public PollerHandle { std::queue<PollerHandle*> handles; @@ -290,6 +290,7 @@ class PollerPrivate { } PollerPrivate() : + alwaysReadableFd(alwaysReadable.getFD()), epollFd(::epoll_create(DefaultFds)), isShutdown(false) { QPID_POSIX_CHECK(epollFd); @@ -328,9 +329,6 @@ class PollerPrivate { } }; -PollerPrivate::ReadablePipe PollerPrivate::alwaysReadable; -int PollerPrivate::alwaysReadableFd = alwaysReadable.getFD(); - void Poller::registerHandle(PollerHandle& handle) { PollerHandlePrivate& eh = *handle.impl; ScopedLock<Mutex> l(eh.lock); diff --git a/cpp/src/qpid/sys/posix/AsynchIO.cpp b/cpp/src/qpid/sys/posix/AsynchIO.cpp index 01ff8b6bfa..31355627cd 100644 --- a/cpp/src/qpid/sys/posix/AsynchIO.cpp +++ b/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -40,6 +40,7 @@ #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> +#include <boost/shared_array.hpp> namespace qpid { namespace sys { @@ -239,6 +240,7 @@ public: virtual void queueForDeletion(); virtual void start(Poller::shared_ptr poller); + virtual void createBuffers(uint32_t size); virtual void queueReadBuffer(BufferBase* buff); virtual void unread(BufferBase* buff); virtual void queueWrite(BufferBase* buff); @@ -270,6 +272,8 @@ private: const Socket& socket; std::deque<BufferBase*> bufferQueue; std::deque<BufferBase*> writeQueue; + std::vector<BufferBase> buffers; + boost::shared_array<char> bufferMemory; bool queuedClose; /** * This flag is used to detect and handle concurrency between @@ -309,15 +313,7 @@ AsynchIO::AsynchIO(const Socket& s, s.setNonblocking(); } -struct deleter -{ - template <typename T> - void operator()(T *ptr){ delete ptr;} -}; - AsynchIO::~AsynchIO() { - std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); - std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); } void AsynchIO::queueForDeletion() { @@ -328,6 +324,19 @@ void AsynchIO::start(Poller::shared_ptr poller) { DispatchHandle::startWatch(poller); } +void AsynchIO::createBuffers(uint32_t size) { + // Allocate all the buffer memory at once + bufferMemory.reset(new char[size*BufferCount]); + + // Create the Buffer structs in a vector + // And push into the buffer queue + buffers.reserve(BufferCount); + for (uint32_t i = 0; i < BufferCount; i++) { + buffers.push_back(BufferBase(&bufferMemory[i*size], size)); + queueReadBuffer(&buffers[i]); + } +} + void AsynchIO::queueReadBuffer(BufferBase* buff) { assert(buff); buff->dataStart = 0; diff --git a/cpp/src/qpid/sys/posix/SystemInfo.cpp b/cpp/src/qpid/sys/posix/SystemInfo.cpp index 2b1bbb97df..cfd2c64aee 100755 --- a/cpp/src/qpid/sys/posix/SystemInfo.cpp +++ b/cpp/src/qpid/sys/posix/SystemInfo.cpp @@ -91,7 +91,7 @@ void SystemInfo::getLocalIpAddresses (uint16_t port, // * The scope id is illegal in URL syntax // * Clients won't be able to use a link local address // without adding their own (potentially different) scope id - sockaddr_in6* sa6 = (sockaddr_in6*)(ifap->ifa_addr); + sockaddr_in6* sa6 = (sockaddr_in6*)((void*)ifap->ifa_addr); if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) break; // Fallthrough } diff --git a/cpp/src/qpid/sys/ssl/SslHandler.cpp b/cpp/src/qpid/sys/ssl/SslHandler.cpp index 8613059f28..eeb8c26a76 100644 --- a/cpp/src/qpid/sys/ssl/SslHandler.cpp +++ b/cpp/src/qpid/sys/ssl/SslHandler.cpp @@ -33,15 +33,6 @@ namespace sys { namespace ssl { -// Buffer definition -struct Buff : public SslIO::BufferBase { - Buff() : - SslIO::BufferBase(new char[65536], 65536) - {} - ~Buff() - { delete [] bytes;} -}; - struct ProtocolTimeoutTask : public sys::TimerTask { SslHandler& handler; std::string id; @@ -78,7 +69,7 @@ SslHandler::~SslHandler() { delete codec; } -void SslHandler::init(SslIO* a, Timer& timer, uint32_t maxTime, int numBuffs) { +void SslHandler::init(SslIO* a, Timer& timer, uint32_t maxTime) { aio = a; // Start timer for this connection @@ -86,17 +77,14 @@ void SslHandler::init(SslIO* a, Timer& timer, uint32_t maxTime, int numBuffs) { timer.add(timeoutTimerTask); // Give connection some buffers to use - for (int i = 0; i < numBuffs; i++) { - aio->queueReadBuffer(new Buff); - } + aio->createBuffers(); } void SslHandler::write(const framing::ProtocolInitiation& data) { QPID_LOG(debug, "SENT [" << identifier << "]: INIT(" << data << ")"); SslIO::BufferBase* buff = aio->getQueuedBuffer(); - if (!buff) - buff = new Buff; + assert(buff); framing::Buffer out(buff->bytes, buff->byteCount); data.encode(out); buff->dataCount = data.encodedSize(); @@ -205,10 +193,11 @@ void SslHandler::idle(SslIO&){ return; } if (codec == 0) return; - if (codec->canEncode()) { - // Try and get a queued buffer if not then construct new one - SslIO::BufferBase* buff = aio->getQueuedBuffer(); - if (!buff) buff = new Buff; + if (!codec->canEncode()) { + return; + } + SslIO::BufferBase* buff = aio->getQueuedBuffer(); + if (buff) { size_t encoded=codec->encode(buff->bytes, buff->byteCount); buff->dataCount = encoded; aio->queueWrite(buff); diff --git a/cpp/src/qpid/sys/ssl/SslHandler.h b/cpp/src/qpid/sys/ssl/SslHandler.h index 74df2b7fb0..14814b0281 100644 --- a/cpp/src/qpid/sys/ssl/SslHandler.h +++ b/cpp/src/qpid/sys/ssl/SslHandler.h @@ -60,7 +60,7 @@ class SslHandler : public OutputControl { public: SslHandler(std::string id, ConnectionCodec::Factory* f, bool nodict); ~SslHandler(); - void init(SslIO* a, Timer& timer, uint32_t maxTime, int numBuffs); + void init(SslIO* a, Timer& timer, uint32_t maxTime); void setClient() { isClient = true; } diff --git a/cpp/src/qpid/sys/ssl/SslIo.cpp b/cpp/src/qpid/sys/ssl/SslIo.cpp index 789c205ead..bbfb703170 100644 --- a/cpp/src/qpid/sys/ssl/SslIo.cpp +++ b/cpp/src/qpid/sys/ssl/SslIo.cpp @@ -197,15 +197,7 @@ SslIO::SslIO(const SslSocket& s, s.setNonblocking(); } -struct deleter -{ - template <typename T> - void operator()(T *ptr){ delete ptr;} -}; - SslIO::~SslIO() { - std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); - std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); } void SslIO::queueForDeletion() { @@ -216,6 +208,19 @@ void SslIO::start(Poller::shared_ptr poller) { DispatchHandle::startWatch(poller); } +void SslIO::createBuffers(uint32_t size) { + // Allocate all the buffer memory at once + bufferMemory.reset(new char[size*BufferCount]); + + // Create the Buffer structs in a vector + // And push into the buffer queue + buffers.reserve(BufferCount); + for (uint32_t i = 0; i < BufferCount; i++) { + buffers.push_back(BufferBase(&bufferMemory[i*size], size)); + queueReadBuffer(&buffers[i]); + } +} + void SslIO::queueReadBuffer(BufferBase* buff) { assert(buff); buff->dataStart = 0; diff --git a/cpp/src/qpid/sys/ssl/SslIo.h b/cpp/src/qpid/sys/ssl/SslIo.h index b795594cd9..f3112bfa65 100644 --- a/cpp/src/qpid/sys/ssl/SslIo.h +++ b/cpp/src/qpid/sys/ssl/SslIo.h @@ -25,6 +25,7 @@ #include "qpid/sys/SecuritySettings.h" #include <boost/function.hpp> +#include <boost/shared_array.hpp> #include <deque> namespace qpid { @@ -87,8 +88,8 @@ private: }; struct SslIOBufferBase { - char* const bytes; - const int32_t byteCount; + char* bytes; + int32_t byteCount; int32_t dataStart; int32_t dataCount; @@ -127,7 +128,9 @@ public: typedef boost::function1<void, SslIO&> IdleCallback; typedef boost::function1<void, SslIO&> RequestCallback; - + SslIO(const SslSocket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0); private: ReadCallback readCallback; EofCallback eofCallback; @@ -138,6 +141,8 @@ private: const SslSocket& socket; std::deque<BufferBase*> bufferQueue; std::deque<BufferBase*> writeQueue; + std::vector<BufferBase> buffers; + boost::shared_array<char> bufferMemory; bool queuedClose; /** * This flag is used to detect and handle concurrency between @@ -148,12 +153,21 @@ private: volatile bool writePending; public: - SslIO(const SslSocket& s, - ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, - ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0); + /* + * Size of IO buffers - this is the maximum possible frame size + 1 + */ + const static uint32_t MaxBufferSize = 65536; + + /* + * Number of IO buffers allocated - I think the code can only use 2 - + * 1 for reading and 1 for writing, allocate 4 for safety + */ + const static uint32_t BufferCount = 4; + void queueForDeletion(); void start(qpid::sys::Poller::shared_ptr poller); + void createBuffers(uint32_t size = MaxBufferSize); void queueReadBuffer(BufferBase* buff); void unread(BufferBase* buff); void queueWrite(BufferBase* buff); diff --git a/cpp/src/qpid/sys/windows/AsynchIO.cpp b/cpp/src/qpid/sys/windows/AsynchIO.cpp index ae53414e52..355acbe0e6 100644 --- a/cpp/src/qpid/sys/windows/AsynchIO.cpp +++ b/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -40,6 +40,7 @@ #include <windows.h> #include <boost/bind.hpp> +#include <boost/shared_array.hpp> namespace { @@ -252,6 +253,7 @@ public: /// Take any actions needed to prepare for working with the poller. virtual void start(Poller::shared_ptr poller); + virtual void createBuffers(uint32_t size); virtual void queueReadBuffer(BufferBase* buff); virtual void unread(BufferBase* buff); virtual void queueWrite(BufferBase* buff); @@ -286,6 +288,8 @@ private: * access to the buffer queue and write queue. */ Mutex bufferQueueLock; + std::vector<BufferBase> buffers; + boost::shared_array<char> bufferMemory; // Number of outstanding I/O operations. volatile LONG opsInProgress; @@ -385,15 +389,7 @@ AsynchIO::AsynchIO(const Socket& s, working(false) { } -struct deleter -{ - template <typename T> - void operator()(T *ptr){ delete ptr;} -}; - AsynchIO::~AsynchIO() { - std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); - std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); } void AsynchIO::queueForDeletion() { @@ -426,6 +422,19 @@ void AsynchIO::start(Poller::shared_ptr poller0) { startReading(); } +void AsynchIO::createBuffers(uint32_t size) { + // Allocate all the buffer memory at once + bufferMemory.reset(new char[size*BufferCount]); + + // Create the Buffer structs in a vector + // And push into the buffer queue + buffers.reserve(BufferCount); + for (uint32_t i = 0; i < BufferCount; i++) { + buffers.push_back(BufferBase(&bufferMemory[i*size], size)); + queueReadBuffer(&buffers[i]); + } +} + void AsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) { assert(buff); buff->dataStart = 0; diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp index 25cc94b290..d263f00ab3 100644 --- a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp +++ b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp @@ -55,7 +55,7 @@ namespace { * the frame layer for writing into. */ struct SslIoBuff : public qpid::sys::AsynchIO::BufferBase { - std::auto_ptr<qpid::sys::AsynchIO::BufferBase> aioBuff; + qpid::sys::AsynchIO::BufferBase* aioBuff; SslIoBuff (qpid::sys::AsynchIO::BufferBase *base, const SecPkgContext_StreamSizes &sizes) @@ -66,7 +66,6 @@ namespace { {} ~SslIoBuff() {} - qpid::sys::AsynchIO::BufferBase* release() { return aioBuff.release(); } }; } @@ -101,10 +100,7 @@ SslAsynchIO::SslAsynchIO(const qpid::sys::Socket& s, } SslAsynchIO::~SslAsynchIO() { - if (leftoverPlaintext) { - delete leftoverPlaintext; - leftoverPlaintext = 0; - } + leftoverPlaintext = 0; } void SslAsynchIO::queueForDeletion() { @@ -121,6 +117,10 @@ void SslAsynchIO::start(qpid::sys::Poller::shared_ptr poller) { startNegotiate(); } +void SslAsynchIO::createBuffers(uint32_t size) { + aio->createBuffers(size); +} + void SslAsynchIO::queueReadBuffer(AsynchIO::BufferBase* buff) { aio->queueReadBuffer(buff); } @@ -148,7 +148,7 @@ void SslAsynchIO::queueWrite(AsynchIO::BufferBase* buff) { // encoding was working on, and adjusting counts for, the SslIoBuff. // Update the count of the original BufferBase before handing off to // the I/O layer. - buff = sslBuff->release(); + buff = sslBuff->aioBuff; SecBuffer buffs[4]; buffs[0].cbBuffer = schSizes.cbHeader; buffs[0].BufferType = SECBUFFER_STREAM_HEADER; diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.h b/cpp/src/qpid/sys/windows/SslAsynchIO.h index edec081ced..e9d9e8d629 100644 --- a/cpp/src/qpid/sys/windows/SslAsynchIO.h +++ b/cpp/src/qpid/sys/windows/SslAsynchIO.h @@ -70,6 +70,7 @@ public: virtual void queueForDeletion(); virtual void start(qpid::sys::Poller::shared_ptr poller); + virtual void createBuffers(uint32_t size); virtual void queueReadBuffer(BufferBase* buff); virtual void unread(BufferBase* buff); virtual void queueWrite(BufferBase* buff); diff --git a/cpp/src/qpid/sys/windows/Time.cpp b/cpp/src/qpid/sys/windows/Time.cpp index 25c50819cd..700a25391f 100644 --- a/cpp/src/qpid/sys/windows/Time.cpp +++ b/cpp/src/qpid/sys/windows/Time.cpp @@ -20,10 +20,12 @@ */ #include "qpid/sys/Time.h" +#include <cmath> #include <ostream> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread_time.hpp> #include <windows.h> +#include <time.h> using namespace boost::posix_time; @@ -33,8 +35,16 @@ namespace { // more or less. Keep track of the start value and the conversion factor to // seconds. bool timeInitialized = false; -LARGE_INTEGER start; -double freq = 1.0; +LARGE_INTEGER start_hpc; +double hpc_freq = 1.0; + +double start_time; + +/// Static constant to remove time skew between FILETIME and POSIX +/// time. POSIX and Win32 use different epochs (Jan. 1, 1970 v.s. +/// Jan. 1, 1601). The following constant defines the difference +/// in 100ns ticks. +const DWORDLONG FILETIME_to_timval_skew = 0x19db1ded53e8000; } @@ -114,23 +124,59 @@ void outputFormattedNow(std::ostream& o) { } void outputHiresNow(std::ostream& o) { + ::time_t tv_sec; + ::tm timeinfo; + char time_string[100]; + if (!timeInitialized) { - start.QuadPart = 0; + // To start, get the current time from FILETIME which includes + // sub-second resolution. However, since FILETIME is updated a bit + // "bumpy" every 15 msec or so, future time displays will be the + // starting FILETIME plus a delta based on the high-resolution + // performance counter. + FILETIME file_time; + ULARGE_INTEGER start_usec; + ::GetSystemTimeAsFileTime(&file_time); // This is in 100ns units + start_usec.LowPart = file_time.dwLowDateTime; + start_usec.HighPart = file_time.dwHighDateTime; + start_usec.QuadPart -= FILETIME_to_timval_skew; + start_usec.QuadPart /= 10; // Convert 100ns to usec + tv_sec = (time_t)(start_usec.QuadPart / (1000 * 1000)); + long tv_usec = (long)(start_usec.QuadPart % (1000 * 1000)); + start_time = static_cast<double>(tv_sec); + start_time += tv_usec / 1000000.0; + + start_hpc.QuadPart = 0; LARGE_INTEGER iFreq; iFreq.QuadPart = 1; - QueryPerformanceCounter(&start); + QueryPerformanceCounter(&start_hpc); QueryPerformanceFrequency(&iFreq); - freq = static_cast<double>(iFreq.QuadPart); + hpc_freq = static_cast<double>(iFreq.QuadPart); timeInitialized = true; } - LARGE_INTEGER iNow; - iNow.QuadPart = 0; - QueryPerformanceCounter(&iNow); - iNow.QuadPart -= start.QuadPart; - if (iNow.QuadPart < 0) - iNow.QuadPart = 0; - double now = static_cast<double>(iNow.QuadPart); - now /= freq; // now is seconds after this - o << std::fixed << std::setprecision(8) << std::setw(16) << std::setfill('0') << now << "s "; + LARGE_INTEGER hpc_now; + hpc_now.QuadPart = 0; + QueryPerformanceCounter(&hpc_now); + hpc_now.QuadPart -= start_hpc.QuadPart; + if (hpc_now.QuadPart < 0) + hpc_now.QuadPart = 0; + double now = static_cast<double>(hpc_now.QuadPart); + now /= hpc_freq; // now is seconds after this + double fnow = start_time + now; + double usec, sec; + usec = modf(fnow, &sec); + tv_sec = static_cast<time_t>(sec); +#ifdef _MSC_VER + ::localtime_s(&timeinfo, &tv_sec); +#else + timeinfo = *(::localtime(&tv_sec)); +#endif + ::strftime(time_string, 100, + "%Y-%m-%d %H:%M:%S", + &timeinfo); + // No way to set "max field width" to cleanly output the double usec so + // convert it back to integral number of usecs and print that. + unsigned long i_usec = usec * 1000 * 1000; + o << time_string << "." << std::setw(6) << std::setfill('0') << i_usec << " "; } }} diff --git a/cpp/src/qpid/xml/XmlExchange.cpp b/cpp/src/qpid/xml/XmlExchange.cpp index 3fb11394d0..f88acb04ee 100644 --- a/cpp/src/qpid/xml/XmlExchange.cpp +++ b/cpp/src/qpid/xml/XmlExchange.cpp @@ -27,6 +27,7 @@ #include "qpid/log/Statement.h" #include "qpid/broker/FedOps.h" +#include "qpid/broker/MapHandler.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/reply_exceptions.h" @@ -198,7 +199,52 @@ bool XmlExchange::unbind(Queue::shared_ptr queue, const std::string& bindingKey, } } -bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args, bool parse_message_content) +namespace { +class DefineExternals : public MapHandler +{ + public: + DefineExternals(DynamicContext* c) : context(c) { assert(context); } + void handleUint8(const MapHandler::CharSequence& key, uint8_t value) { process(std::string(key.data, key.size), (int) value); } + void handleUint16(const MapHandler::CharSequence& key, uint16_t value) { process(std::string(key.data, key.size), (int) value); } + void handleUint32(const MapHandler::CharSequence& key, uint32_t value) { process(std::string(key.data, key.size), (int) value); } + void handleUint64(const MapHandler::CharSequence& key, uint64_t value) { process(std::string(key.data, key.size), (int) value); } + void handleInt8(const MapHandler::CharSequence& key, int8_t value) { process(std::string(key.data, key.size), (int) value); } + void handleInt16(const MapHandler::CharSequence& key, int16_t value) { process(std::string(key.data, key.size), (int) value); } + void handleInt32(const MapHandler::CharSequence& key, int32_t value) { process(std::string(key.data, key.size), (int) value); } + void handleInt64(const MapHandler::CharSequence& key, int64_t value) { process(std::string(key.data, key.size), (int) value); } + void handleFloat(const MapHandler::CharSequence& key, float value) { process(std::string(key.data, key.size), value); } + void handleDouble(const MapHandler::CharSequence& key, double value) { process(std::string(key.data, key.size), value); } + void handleString(const MapHandler::CharSequence& key, const MapHandler::CharSequence& value, const MapHandler::CharSequence& /*encoding*/) + { + process(std::string(key.data, key.size), std::string(value.data, value.size)); + } + void handleVoid(const MapHandler::CharSequence&) {} + private: + void process(const std::string& key, double value) + { + QPID_LOG(trace, "XmlExchange, external variable (double): " << key << " = " << value); + Item::Ptr item = context->getItemFactory()->createDouble(value, context); + context->setExternalVariable(X(key.c_str()), item); + } + void process(const std::string& key, int value) + { + QPID_LOG(trace, "XmlExchange, external variable (int):" << key << " = " << value); + Item::Ptr item = context->getItemFactory()->createInteger(value, context); + context->setExternalVariable(X(key.c_str()), item); + } + void process(const std::string& key, const std::string& value) + { + QPID_LOG(trace, "XmlExchange, external variable (string):" << key << " = " << value); + Item::Ptr item = context->getItemFactory()->createString(X(value.c_str()), context); + context->setExternalVariable(X(key.c_str()), item); + } + + DynamicContext* context; +}; + +} + +bool XmlExchange::matches(Query& query, Deliverable& msg, bool parse_message_content) { std::string msgContent; @@ -212,7 +258,7 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F if (parse_message_content) { - msg.getMessage().getFrames().getContent(msgContent); + msgContent = msg.getMessage().getContent(); QPID_LOG(trace, "matches: message content is [" << msgContent << "]"); @@ -231,28 +277,8 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F } } - if (args) { - FieldTable::ValueMap::const_iterator v = args->begin(); - for(; v != args->end(); ++v) { - - if (v->second->convertsTo<double>()) { - QPID_LOG(trace, "XmlExchange, external variable (double): " << v->first << " = " << v->second->get<double>()); - Item::Ptr value = context->getItemFactory()->createDouble(v->second->get<double>(), context.get()); - context->setExternalVariable(X(v->first.c_str()), value); - } - else if (v->second->convertsTo<int>()) { - QPID_LOG(trace, "XmlExchange, external variable (int):" << v->first << " = " << v->second->getData().getInt()); - Item::Ptr value = context->getItemFactory()->createInteger(v->second->get<int>(), context.get()); - context->setExternalVariable(X(v->first.c_str()), value); - } - else if (v->second->convertsTo<std::string>()) { - QPID_LOG(trace, "XmlExchange, external variable (string):" << v->first << " = " << v->second->getData().getString().c_str()); - Item::Ptr value = context->getItemFactory()->createString(X(v->second->get<std::string>().c_str()), context.get()); - context->setExternalVariable(X(v->first.c_str()), value); - } - - } - } + DefineExternals f(context.get()); + msg.getMessage().processProperties(f); Result result = query->execute(context.get()); #ifdef XQ_EFFECTIVE_BOOLEAN_VALUE_HPP @@ -286,7 +312,6 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F void XmlExchange::route(Deliverable& msg) { const std::string& routingKey = msg.getMessage().getRoutingKey(); - const FieldTable* args = msg.getMessage().getApplicationHeaders(); PreRoute pr(msg, this); try { XmlBinding::vector::ConstPtr p; @@ -298,7 +323,7 @@ void XmlExchange::route(Deliverable& msg) } for (std::vector<XmlBinding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); i++) { - if (matches((*i)->xquery, msg, args, (*i)->parse_message_content)) { + if (matches((*i)->xquery, msg, (*i)->parse_message_content)) { b->push_back(*i); } } diff --git a/cpp/src/qpid/xml/XmlExchange.h b/cpp/src/qpid/xml/XmlExchange.h index 1d4723f9c4..7b04781ad5 100644 --- a/cpp/src/qpid/xml/XmlExchange.h +++ b/cpp/src/qpid/xml/XmlExchange.h @@ -65,7 +65,7 @@ class XmlExchange : public virtual Exchange { qpid::sys::RWlock lock; - bool matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args, bool parse_message_content); + bool matches(Query& query, Deliverable& msg, bool parse_message_content); public: static const std::string typeName; diff --git a/cpp/src/replication.mk b/cpp/src/replication.mk deleted file mode 100644 index e5da32f88b..0000000000 --- a/cpp/src/replication.mk +++ /dev/null @@ -1,52 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Make file for building two plugins for asynchronously replicating -# queues. - -dmoduleexec_LTLIBRARIES += replicating_listener.la replication_exchange.la - -# a queue event listener plugin that creates messages on a replication -# queue corresponding to enqueue and dequeue events: -replicating_listener_la_SOURCES = \ - qpid/replication/constants.h \ - qpid/replication/ReplicatingEventListener.cpp \ - qpid/replication/ReplicatingEventListener.h - -replicating_listener_la_LIBADD = libqpidbroker.la -if SUNOS - replicating_listener_la_LIBADD += libqpidcommon.la -lboost_program_options -luuid $(SUNCC_RUNTIME_LIBS) -endif -replicating_listener_la_LDFLAGS = $(PLUGINLDFLAGS) - -# a custom exchange plugin that allows an exchange to be created that -# can process the messages from a replication queue (populated on the -# source system by the replicating listener plugin above) and take the -# corresponding action on the local queues -replication_exchange_la_SOURCES = \ - qpid/replication/constants.h \ - qpid/replication/ReplicationExchange.cpp \ - qpid/replication/ReplicationExchange.h - -replication_exchange_la_LIBADD = libqpidbroker.la - -if SUNOS - replication_exchange_la_LIBADD += libqpidcommon.la -lboost_program_options $(SUNCC_RUNTIME_LIBS) -luuid -endif -replication_exchange_la_LDFLAGS = $(PLUGINLDFLAGS) - diff --git a/cpp/src/tests/CMakeLists.txt b/cpp/src/tests/CMakeLists.txt index 29dfe3634f..be7864fcb4 100644 --- a/cpp/src/tests/CMakeLists.txt +++ b/cpp/src/tests/CMakeLists.txt @@ -126,6 +126,7 @@ set(unit_tests_to_build ExchangeTest HeadersExchangeTest MessageTest + QueueDepth QueueRegistryTest QueuePolicyTest QueueFlowLimitTest @@ -135,16 +136,12 @@ set(unit_tests_to_build TimerTest TopicExchangeTest TxBufferTest - TxPublishTest - MessageBuilderTest ManagementTest MessageReplayTracker ConsoleTest - QueueEvents ProxyTest RetryList FrameDecoder - ReplicationTest ClientMessageTest PollableCondition Variant @@ -165,10 +162,6 @@ remember_location(unit_test) add_library (shlibtest MODULE shlibtest.cpp) -if (BUILD_CLUSTER) - include (cluster.cmake) -endif (BUILD_CLUSTER) - # FIXME aconway 2009-11-30: enable SSL #if SSL #include ssl.mk diff --git a/cpp/src/tests/ClientSessionTest.cpp b/cpp/src/tests/ClientSessionTest.cpp index 1905219bf2..1f07d2b83f 100644 --- a/cpp/src/tests/ClientSessionTest.cpp +++ b/cpp/src/tests/ClientSessionTest.cpp @@ -621,7 +621,7 @@ QPID_AUTO_TEST_CASE(testQueueDeleted) fix.session.queueDeclare(arg::queue="my-queue"); LocalQueue queue; fix.subs.subscribe(queue, "my-queue"); - + ScopedSuppressLogging sl; fix.session.queueDelete(arg::queue="my-queue"); BOOST_CHECK_THROW(queue.get(1*qpid::sys::TIME_SEC), qpid::framing::ResourceDeletedException); diff --git a/cpp/src/tests/DeliveryRecordTest.cpp b/cpp/src/tests/DeliveryRecordTest.cpp index fb7bd2f727..c83bd9a6a4 100644 --- a/cpp/src/tests/DeliveryRecordTest.cpp +++ b/cpp/src/tests/DeliveryRecordTest.cpp @@ -49,7 +49,7 @@ QPID_AUTO_TEST_CASE(testSort) list<DeliveryRecord> records; for (list<SequenceNumber>::iterator i = ids.begin(); i != ids.end(); i++) { - DeliveryRecord r(QueuedMessage(0), Queue::shared_ptr(), "tag", Consumer::shared_ptr(), false, false, false); + DeliveryRecord r(QueueCursor(CONSUMER), framing::SequenceNumber(), Queue::shared_ptr(), "tag", Consumer::shared_ptr(), false, false, false); r.setId(*i); records.push_back(r); } diff --git a/cpp/src/tests/ExchangeTest.cpp b/cpp/src/tests/ExchangeTest.cpp index 66a16b9178..4f18b91b5a 100644 --- a/cpp/src/tests/ExchangeTest.cpp +++ b/cpp/src/tests/ExchangeTest.cpp @@ -35,7 +35,6 @@ using std::string; -using boost::intrusive_ptr; using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; @@ -62,11 +61,9 @@ QPID_AUTO_TEST_CASE(testMe) queue.reset(); queue2.reset(); - intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "abc", false, "id")); - DeliverableMessage msg(msgPtr); + DeliverableMessage msg(MessageUtils::createMessage("exchange", "abc"), 0); topic.route(msg); direct.route(msg); - } QPID_AUTO_TEST_CASE(testIsBound) @@ -170,16 +167,6 @@ QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) BOOST_CHECK_EQUAL(string("direct"), response.first->getType()); } -intrusive_ptr<Message> cmessage(std::string exchange, std::string routingKey) { - intrusive_ptr<Message> msg(new Message()); - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - msg->getFrames().append(method); - msg->getFrames().append(header); - msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); - return msg; -} - QPID_AUTO_TEST_CASE(testSequenceOptions) { FieldTable args; @@ -189,46 +176,35 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) { DirectExchange direct("direct1", false, args); - 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); + DeliverableMessage msg1(MessageUtils::createMessage("e", "abc"), 0); + DeliverableMessage msg2(MessageUtils::createMessage("e", "abc"), 0); + DeliverableMessage msg3(MessageUtils::createMessage("e", "abc"), 0); - direct.route(dmsg1); - direct.route(dmsg2); - direct.route(dmsg3); + direct.route(msg1); + direct.route(msg2); + direct.route(msg3); - BOOST_CHECK_EQUAL(1, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - BOOST_CHECK_EQUAL(2, msg2->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - BOOST_CHECK_EQUAL(3, msg3->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + BOOST_CHECK_EQUAL(1, msg1.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); + BOOST_CHECK_EQUAL(2, msg2.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); + BOOST_CHECK_EQUAL(3, msg3.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); FanOutExchange fanout("fanout1", false, args); HeadersExchange header("headers1", false, args); TopicExchange topic ("topic1", false, args); // check other exchanges, that they preroute - intrusive_ptr<Message> msg4 = cmessage("e", "abc"); - intrusive_ptr<Message> msg5 = cmessage("e", "abc"); + DeliverableMessage msg4(MessageUtils::createMessage("e", "abc"), 0); + DeliverableMessage msg5(MessageUtils::createMessage("e", "abc"), 0); + DeliverableMessage msg6(MessageUtils::createMessage("e", "abc"), 0); - // Need at least empty header for the HeadersExchange to route at all - msg5->insertCustomProperty("", ""); - intrusive_ptr<Message> msg6 = cmessage("e", "abc"); + fanout.route(msg4); + BOOST_CHECK_EQUAL(1, msg4.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); - DeliverableMessage dmsg4(msg4); - DeliverableMessage dmsg5(msg5); - DeliverableMessage dmsg6(msg6); + header.route(msg5); + BOOST_CHECK_EQUAL(1, msg5.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); - fanout.route(dmsg4); - BOOST_CHECK_EQUAL(1, msg4->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - - header.route(dmsg5); - BOOST_CHECK_EQUAL(1, msg5->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); - - topic.route(dmsg6); - BOOST_CHECK_EQUAL(1, msg6->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + topic.route(msg6); + BOOST_CHECK_EQUAL(1, msg6.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); direct.encode(buffer); } { @@ -237,11 +213,10 @@ QPID_AUTO_TEST_CASE(testSequenceOptions) buffer.reset(); DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer); - intrusive_ptr<Message> msg1 = cmessage("e", "abc"); - DeliverableMessage dmsg1(msg1); - exch_dec->route(dmsg1); + DeliverableMessage msg1(MessageUtils::createMessage("e", "abc"), 0); + exch_dec->route(msg1); - BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence")); + BOOST_CHECK_EQUAL(4, msg1.getMessage().getAnnotation("qpid.msg_sequence").asInt64()); } delete [] buff; @@ -256,9 +231,11 @@ QPID_AUTO_TEST_CASE(testIVEOption) HeadersExchange header("headers1", false, args); TopicExchange topic ("topic1", false, args); - intrusive_ptr<Message> msg1 = cmessage("direct1", "abc"); - msg1->insertCustomProperty("a", "abc"); - DeliverableMessage dmsg1(msg1); + qpid::types::Variant::Map properties; + properties["routing-key"] = "abc"; + properties["a"] = "abc"; + Message msg1 = MessageUtils::createMessage(properties, "my-message", "direct1"); + DeliverableMessage dmsg1(msg1, 0); FieldTable args2; args2.setString("x-match", "any"); @@ -273,8 +250,6 @@ QPID_AUTO_TEST_CASE(testIVEOption) Queue::shared_ptr queue2(new Queue("queue2", true)); Queue::shared_ptr queue3(new Queue("queue3", true)); - BOOST_CHECK(HeadersExchange::match(args2, msg1->getProperties<MessageProperties>()->getApplicationHeaders())); - BOOST_CHECK(direct.bind(queue, "abc", 0)); BOOST_CHECK(fanout.bind(queue1, "abc", 0)); BOOST_CHECK(header.bind(queue2, "", &args2)); @@ -287,7 +262,6 @@ QPID_AUTO_TEST_CASE(testIVEOption) } - QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am index cdc7429f3b..f9eed9270b 100644 --- a/cpp/src/tests/Makefile.am +++ b/cpp/src/tests/Makefile.am @@ -96,6 +96,7 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ ExchangeTest.cpp \ HeadersExchangeTest.cpp \ MessageTest.cpp \ + QueueDepth.cpp \ QueueRegistryTest.cpp \ QueuePolicyTest.cpp \ QueueFlowLimitTest.cpp \ @@ -105,19 +106,15 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ TimerTest.cpp \ TopicExchangeTest.cpp \ TxBufferTest.cpp \ - TxPublishTest.cpp \ - MessageBuilderTest.cpp \ ConnectionOptions.h \ ForkedBroker.h \ ForkedBroker.cpp \ ManagementTest.cpp \ MessageReplayTracker.cpp \ ConsoleTest.cpp \ - QueueEvents.cpp \ ProxyTest.cpp \ RetryList.cpp \ FrameDecoder.cpp \ - ReplicationTest.cpp \ ClientMessageTest.cpp \ PollableCondition.cpp \ Variant.cpp \ @@ -142,7 +139,6 @@ test_store_la_SOURCES = test_store.cpp test_store_la_LIBADD = $(lib_broker) test_store_la_LDFLAGS = -module -include cluster.mk include sasl.mk if SSL include ssl.mk @@ -338,7 +334,6 @@ EXTRA_DIST += \ dynamic_log_level_test \ qpid-ctrl \ CMakeLists.txt \ - cluster.cmake \ windows/DisableWin32ErrorWindows.cpp \ background.ps1 \ find_prog.ps1 \ @@ -372,14 +367,6 @@ LONG_TESTS+=start_broker \ stop_broker \ 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 \ shared_perftest \ diff --git a/cpp/src/tests/MessageBuilderTest.cpp b/cpp/src/tests/MessageBuilderTest.cpp deleted file mode 100644 index 9adb133d40..0000000000 --- a/cpp/src/tests/MessageBuilderTest.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/broker/Message.h" -#include "qpid/broker/MessageBuilder.h" -#include "qpid/broker/NullMessageStore.h" -#include "qpid/framing/frame_functors.h" -#include "qpid/framing/MessageTransferBody.h" -#include "qpid/framing/TypeFilter.h" -#include "unit_test.h" -#include <list> - -using namespace qpid::broker; -using namespace qpid::framing; -using namespace qpid::sys; - -namespace qpid { -namespace tests { - -class MockMessageStore : public NullMessageStore -{ - enum Op {STAGE=1, APPEND=2}; - - uint64_t id; - boost::intrusive_ptr<PersistableMessage> expectedMsg; - std::string expectedData; - std::list<Op> ops; - - void checkExpectation(Op actual) - { - BOOST_CHECK_EQUAL(ops.front(), actual); - ops.pop_front(); - } - - public: - MockMessageStore() : id(0), expectedMsg(0) {} - - void expectStage(PersistableMessage& msg) - { - expectedMsg = &msg; - ops.push_back(STAGE); - } - - void expectAppendContent(PersistableMessage& msg, const std::string& data) - { - expectedMsg = &msg; - expectedData = data; - ops.push_back(APPEND); - } - - void stage(const boost::intrusive_ptr<PersistableMessage>& msg) - { - checkExpectation(STAGE); - BOOST_CHECK_EQUAL(expectedMsg, msg); - msg->setPersistenceId(++id); - } - - void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, - const std::string& data) - { - checkExpectation(APPEND); - BOOST_CHECK_EQUAL(boost::static_pointer_cast<const PersistableMessage>(expectedMsg), msg); - BOOST_CHECK_EQUAL(expectedData, data); - } - - bool expectationsMet() - { - return ops.empty(); - } - - //don't treat this store as a null impl - bool isNull() const - { - return false; - } - -}; - -QPID_AUTO_TEST_SUITE(MessageBuilderTestSuite) - -QPID_AUTO_TEST_CASE(testHeaderOnly) -{ - MessageBuilder builder(0); - builder.start(SequenceNumber()); - - std::string exchange("builder-exchange"); - std::string key("builder-exchange"); - - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0); - header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); - - builder.handle(method); - builder.handle(header); - - BOOST_CHECK(builder.getMessage()); - BOOST_CHECK_EQUAL(exchange, builder.getMessage()->getExchangeName()); - BOOST_CHECK_EQUAL(key, builder.getMessage()->getRoutingKey()); - BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); -} - -QPID_AUTO_TEST_CASE(test1ContentFrame) -{ - MessageBuilder builder(0); - builder.start(SequenceNumber()); - - std::string data("abcdefg"); - std::string exchange("builder-exchange"); - std::string key("builder-exchange"); - - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - AMQFrame content((AMQContentBody(data))); - method.setEof(false); - header.setBof(false); - header.setEof(false); - content.setBof(false); - - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size()); - header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); - - builder.handle(method); - BOOST_CHECK(builder.getMessage()); - BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); - - builder.handle(header); - BOOST_CHECK(builder.getMessage()); - BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); - - builder.handle(content); - BOOST_CHECK(builder.getMessage()); - BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); -} - -QPID_AUTO_TEST_CASE(test2ContentFrames) -{ - MessageBuilder builder(0); - builder.start(SequenceNumber()); - - std::string data1("abcdefg"); - std::string data2("hijklmn"); - std::string exchange("builder-exchange"); - std::string key("builder-exchange"); - - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - AMQFrame content1((AMQContentBody(data1))); - AMQFrame content2((AMQContentBody(data2))); - method.setEof(false); - header.setBof(false); - header.setEof(false); - content1.setBof(false); - content1.setEof(false); - content2.setBof(false); - - header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); - header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); - - builder.handle(method); - builder.handle(header); - builder.handle(content1); - BOOST_CHECK(builder.getMessage()); - BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); - - builder.handle(content2); - BOOST_CHECK(builder.getMessage()); - BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); -} -QPID_AUTO_TEST_SUITE_END() - -}} // namespace qpid::tests diff --git a/cpp/src/tests/MessageTest.cpp b/cpp/src/tests/MessageTest.cpp index 3a3ed061f9..fe670a274e 100644 --- a/cpp/src/tests/MessageTest.cpp +++ b/cpp/src/tests/MessageTest.cpp @@ -24,6 +24,7 @@ #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/Uuid.h" +#include "MessageUtils.h" #include "unit_test.h" @@ -43,49 +44,29 @@ QPID_AUTO_TEST_CASE(testEncodeDecode) { string exchange = "MyExchange"; string routingKey = "MyRoutingKey"; + uint64_t ttl(60); Uuid messageId(true); - string data1("abcdefg"); - string data2("hijklmn"); + string data("abcdefghijklmn"); - boost::intrusive_ptr<Message> msg(new Message()); + qpid::types::Variant::Map properties; + properties["routing-key"] = routingKey; + properties["ttl"] = ttl; + properties["durable"] = true; + properties["message-id"] = qpid::types::Uuid(messageId.data()); + properties["abc"] = "xyz"; + Message msg = MessageUtils::createMessage(properties, data); - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - AMQFrame content1((AMQContentBody(data1))); - AMQFrame content2((AMQContentBody(data2))); + std::string buffer; + encode(msg, buffer); + msg = Message(); + decode(buffer, msg); - msg->getFrames().append(method); - msg->getFrames().append(header); - msg->getFrames().append(content1); - msg->getFrames().append(content2); - - MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true); - mProps->setContentLength(data1.size() + data2.size()); - mProps->setMessageId(messageId); - FieldTable applicationHeaders; - applicationHeaders.setString("abc", "xyz"); - mProps->setApplicationHeaders(applicationHeaders); - DeliveryProperties* dProps = msg->getFrames().getHeaders()->get<DeliveryProperties>(true); - dProps->setRoutingKey(routingKey); - dProps->setDeliveryMode(PERSISTENT); - BOOST_CHECK(msg->isPersistent()); - - std::vector<char> buff(msg->encodedSize()); - Buffer wbuffer(&buff[0], msg->encodedSize()); - msg->encode(wbuffer); - - Buffer rbuffer(&buff[0], msg->encodedSize()); - msg = new Message(); - msg->decodeHeader(rbuffer); - msg->decodeContent(rbuffer); - BOOST_CHECK_EQUAL(exchange, msg->getExchangeName()); - BOOST_CHECK_EQUAL(routingKey, msg->getRoutingKey()); - BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->contentSize()); - BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->getProperties<MessageProperties>()->getContentLength()); - BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId()); - BOOST_CHECK_EQUAL(string("xyz"), msg->getProperties<MessageProperties>()->getApplicationHeaders().getAsString("abc")); - BOOST_CHECK_EQUAL((uint8_t) PERSISTENT, msg->getProperties<DeliveryProperties>()->getDeliveryMode()); - BOOST_CHECK(msg->isPersistent()); + BOOST_CHECK_EQUAL(routingKey, msg.getRoutingKey()); + BOOST_CHECK_EQUAL((uint64_t) data.size(), msg.getContentSize()); + BOOST_CHECK_EQUAL(data, msg.getContent()); + //BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId()); + BOOST_CHECK_EQUAL(string("xyz"), msg.getPropertyAsString("abc")); + BOOST_CHECK(msg.isPersistent()); } QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/MessageUtils.h b/cpp/src/tests/MessageUtils.h index 991e2a2714..c2eabd804d 100644 --- a/cpp/src/tests/MessageUtils.h +++ b/cpp/src/tests/MessageUtils.h @@ -20,9 +20,11 @@ */ #include "qpid/broker/Message.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/Uuid.h" +#include "qpid/types/Variant.h" using namespace qpid; using namespace broker; @@ -33,11 +35,46 @@ namespace tests { struct MessageUtils { - static boost::intrusive_ptr<Message> createMessage(const std::string& exchange="", const std::string& routingKey="", - const bool durable = false, const Uuid& messageId=Uuid(true), - uint64_t contentSize = 0) + static Message createMessage(const qpid::types::Variant::Map& properties, const std::string& content="", const std::string& destination = "") { - boost::intrusive_ptr<broker::Message> msg(new broker::Message()); + boost::intrusive_ptr<broker::amqp_0_10::MessageTransfer> msg(new broker::amqp_0_10::MessageTransfer()); + + AMQFrame method(( MessageTransferBody(ProtocolVersion(), destination, 0, 0))); + AMQFrame header((AMQHeaderBody())); + + msg->getFrames().append(method); + msg->getFrames().append(header); + if (content.size()) { + msg->getFrames().getHeaders()->get<MessageProperties>(true)->setContentLength(content.size()); + AMQFrame data((AMQContentBody(content))); + msg->getFrames().append(data); + } + for (qpid::types::Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) { + if (i->first == "routing-key" && !i->second.isVoid()) { + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(i->second); + } else if (i->first == "message-id" && !i->second.isVoid()) { + qpid::types::Uuid id = i->second; + qpid::framing::Uuid id2(id.data()); + msg->getFrames().getHeaders()->get<MessageProperties>(true)->setMessageId(id2); + } else if (i->first == "ttl" && !i->second.isVoid()) { + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setTtl(i->second); + } else if (i->first == "priority" && !i->second.isVoid()) { + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setPriority(i->second); + } else if (i->first == "durable" && !i->second.isVoid()) { + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(i->second.asBool() ? 2 : 1); + } else { + msg->getFrames().getHeaders()->get<MessageProperties>(true)->getApplicationHeaders().setString(i->first, i->second); + } + } + return Message(msg, msg); + } + + + static Message createMessage(const std::string& exchange="", const std::string& routingKey="", + uint64_t ttl = 0, bool durable = false, const Uuid& messageId=Uuid(true), + const std::string& content="") + { + boost::intrusive_ptr<broker::amqp_0_10::MessageTransfer> msg(new broker::amqp_0_10::MessageTransfer()); AMQFrame method(( MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); AMQFrame header((AMQHeaderBody())); @@ -45,18 +82,18 @@ struct MessageUtils msg->getFrames().append(method); msg->getFrames().append(header); MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true); - props->setContentLength(contentSize); + props->setContentLength(content.size()); props->setMessageId(messageId); msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); if (durable) msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(2); - return msg; - } - - static void addContent(boost::intrusive_ptr<Message> msg, const std::string& data) - { - AMQFrame content((AMQContentBody(data))); - msg->getFrames().append(content); + if (ttl) + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setTtl(ttl); + if (content.size()) { + AMQFrame data((AMQContentBody(content))); + msg->getFrames().append(data); + } + return Message(msg, msg); } }; diff --git a/cpp/src/tests/QueueDepth.cpp b/cpp/src/tests/QueueDepth.cpp new file mode 100644 index 0000000000..09b221b3a8 --- /dev/null +++ b/cpp/src/tests/QueueDepth.cpp @@ -0,0 +1,105 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "qpid/broker/QueueDepth.h" + +#include "unit_test.h" + +namespace qpid { +namespace tests { + +QPID_AUTO_TEST_SUITE(QueueDepthTestSuite) + +using namespace qpid::broker; + +QPID_AUTO_TEST_CASE(testCompare) +{ + QueueDepth a(0, 0); + QueueDepth b(1, 1); + QueueDepth c(2, 2); + QueueDepth d(1, 1); + + BOOST_CHECK(a < b); + BOOST_CHECK(b < c); + BOOST_CHECK(a < c); + + BOOST_CHECK(b > a); + BOOST_CHECK(c > b); + BOOST_CHECK(c > a); + + BOOST_CHECK(b == d); + BOOST_CHECK(d == b); + BOOST_CHECK(a != b); + BOOST_CHECK(b != a); + + QueueDepth e; e.setCount(1); + QueueDepth f; f.setCount(2); + BOOST_CHECK(e < f); + BOOST_CHECK(f > e); + + QueueDepth g; g.setSize(1); + QueueDepth h; h.setSize(2); + BOOST_CHECK(g < h); + BOOST_CHECK(h > g); +} + +QPID_AUTO_TEST_CASE(testIncrement) +{ + QueueDepth a(5, 10); + QueueDepth b(3, 6); + QueueDepth c(8, 16); + a += b; + BOOST_CHECK(a == c); + BOOST_CHECK_EQUAL(8u, a.getCount()); + BOOST_CHECK_EQUAL(16u, a.getSize()); +} + +QPID_AUTO_TEST_CASE(testDecrement) +{ + QueueDepth a(5, 10); + QueueDepth b(3, 6); + QueueDepth c(2, 4); + a -= b; + BOOST_CHECK(a == c); + BOOST_CHECK_EQUAL(2u, a.getCount()); + BOOST_CHECK_EQUAL(4u, a.getSize()); +} + +QPID_AUTO_TEST_CASE(testAddition) +{ + QueueDepth a(5, 10); + QueueDepth b(3, 6); + + QueueDepth c = a + b; + BOOST_CHECK_EQUAL(8u, c.getCount()); + BOOST_CHECK_EQUAL(16u, c.getSize()); +} + +QPID_AUTO_TEST_CASE(testSubtraction) +{ + QueueDepth a(5, 10); + QueueDepth b(3, 6); + + QueueDepth c = a - b; + BOOST_CHECK_EQUAL(2u, c.getCount()); + BOOST_CHECK_EQUAL(4u, c.getSize()); +} + +QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/cpp/src/tests/QueueEvents.cpp b/cpp/src/tests/QueueEvents.cpp deleted file mode 100644 index cea8bbf0db..0000000000 --- a/cpp/src/tests/QueueEvents.cpp +++ /dev/null @@ -1,238 +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 "MessageUtils.h" -#include "unit_test.h" -#include "BrokerFixture.h" -#include "qpid/broker/Message.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/QueueEvents.h" -#include "qpid/client/QueueOptions.h" -#include "qpid/framing/SequenceNumber.h" -#include "qpid/sys/Dispatcher.h" -#include <boost/bind.hpp> -#include <boost/format.hpp> - -namespace qpid { -namespace tests { - -QPID_AUTO_TEST_SUITE(QueueEventsSuite) - -using namespace qpid::client; -using namespace qpid::broker; -using namespace qpid::sys; -using qpid::framing::SequenceNumber; - -struct EventChecker -{ - typedef std::deque<QueueEvents::Event> Events; - - Events events; - boost::shared_ptr<Poller> poller; - - void handle(QueueEvents::Event e) - { - if (events.empty()) { - BOOST_FAIL("Unexpected event received"); - } else { - BOOST_CHECK_EQUAL(events.front().type, e.type); - BOOST_CHECK_EQUAL(events.front().msg.queue, e.msg.queue); - BOOST_CHECK_EQUAL(events.front().msg.payload, e.msg.payload); - BOOST_CHECK_EQUAL(events.front().msg.position, e.msg.position); - events.pop_front(); - } - if (events.empty() && poller) poller->shutdown(); - } - - void expect(QueueEvents::Event e) - { - events.push_back(e); - } -}; - -QPID_AUTO_TEST_CASE(testBasicEventProcessing) -{ - boost::shared_ptr<Poller> poller(new Poller()); - sys::Dispatcher dispatcher(poller); - Thread dispatchThread(dispatcher); - QueueEvents events(poller); - EventChecker listener; - listener.poller = poller; - events.registerListener("dummy", boost::bind(&EventChecker::handle, &listener, _1)); - //signal occurence of some events: - Queue queue("queue1"); - SequenceNumber id; - QueuedMessage event1(&queue, MessageUtils::createMessage(), id); - QueuedMessage event2(&queue, MessageUtils::createMessage(), ++id); - - //define events expected by listener: - listener.expect(QueueEvents::Event(QueueEvents::ENQUEUE, event1)); - listener.expect(QueueEvents::Event(QueueEvents::ENQUEUE, event2)); - listener.expect(QueueEvents::Event(QueueEvents::DEQUEUE, event1)); - - events.enqueued(event1); - events.enqueued(event2); - events.dequeued(event1); - - dispatchThread.join(); - events.shutdown(); - events.unregisterListener("dummy"); -} - - -struct EventRecorder -{ - struct EventRecord - { - QueueEvents::EventType type; - std::string queue; - std::string content; - SequenceNumber position; - }; - - typedef std::deque<EventRecord> Events; - - Events events; - - void handle(QueueEvents::Event event) - { - EventRecord record; - record.type = event.type; - record.queue = event.msg.queue->getName(); - event.msg.payload->getFrames().getContent(record.content); - record.position = event.msg.position; - events.push_back(record); - } - - void check(QueueEvents::EventType type, const std::string& queue, const std::string& content, const SequenceNumber& position) - { - if (events.empty()) { - BOOST_FAIL("Missed event"); - } else { - BOOST_CHECK_EQUAL(events.front().type, type); - BOOST_CHECK_EQUAL(events.front().queue, queue); - BOOST_CHECK_EQUAL(events.front().content, content); - BOOST_CHECK_EQUAL(events.front().position, position); - events.pop_front(); - } - } - void checkEnqueue(const std::string& queue, const std::string& data, const SequenceNumber& position) - { - check(QueueEvents::ENQUEUE, queue, data, position); - } - - void checkDequeue(const std::string& queue, const std::string& data, const SequenceNumber& position) - { - check(QueueEvents::DEQUEUE, queue, data, position); - } -}; - -QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing) -{ - SessionFixture fixture; - //register dummy event listener to broker - EventRecorder listener; - fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1)); - - //declare queue with event options specified - QueueOptions options; - options.enableQueueEvents(false); - std::string q("queue-events-test"); - fixture.session.queueDeclare(arg::queue=q, arg::arguments=options); - //send and consume some messages - LocalQueue incoming; - Subscription sub = fixture.subs.subscribe(incoming, q); - for (int i = 0; i < 5; i++) { - fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); - } - for (int i = 0; i < 3; i++) { - BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); - } - for (int i = 5; i < 10; i++) { - fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); - } - for (int i = 3; i < 10; i++) { - BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); - } - fixture.connection.close(); - fixture.broker->getQueueEvents().shutdown(); - - //check listener was notified of all events, and in correct order - SequenceNumber enqueueId(1); - SequenceNumber dequeueId(1); - for (int i = 0; i < 5; i++) { - listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } - for (int i = 0; i < 3; i++) { - listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++); - } - for (int i = 5; i < 10; i++) { - listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } - for (int i = 3; i < 10; i++) { - listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++); - } -} - -QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly) -{ - SessionFixture fixture; - //register dummy event listener to broker - EventRecorder listener; - fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1)); - - //declare queue with event options specified - QueueOptions options; - options.enableQueueEvents(true); - std::string q("queue-events-test"); - fixture.session.queueDeclare(arg::queue=q, arg::arguments=options); - //send and consume some messages - LocalQueue incoming; - Subscription sub = fixture.subs.subscribe(incoming, q); - for (int i = 0; i < 5; i++) { - fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); - } - for (int i = 0; i < 3; i++) { - BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); - } - for (int i = 5; i < 10; i++) { - fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q)); - } - for (int i = 3; i < 10; i++) { - BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str()); - } - fixture.connection.close(); - fixture.broker->getQueueEvents().shutdown(); - - //check listener was notified of all events, and in correct order - SequenceNumber enqueueId(1); - SequenceNumber dequeueId(1); - for (int i = 0; i < 5; i++) { - listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } - for (int i = 5; i < 10; i++) { - listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++); - } -} - -QPID_AUTO_TEST_SUITE_END() - -}} // namespace qpid::tests diff --git a/cpp/src/tests/QueueFlowLimitTest.cpp b/cpp/src/tests/QueueFlowLimitTest.cpp index bd868398f8..d305ca452b 100644 --- a/cpp/src/tests/QueueFlowLimitTest.cpp +++ b/cpp/src/tests/QueueFlowLimitTest.cpp @@ -23,8 +23,8 @@ #include "unit_test.h" #include "test_tools.h" -#include "qpid/broker/QueuePolicy.h" #include "qpid/broker/QueueFlowLimit.h" +#include "qpid/broker/QueueSettings.h" #include "qpid/sys/Time.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/FieldValue.h" @@ -66,21 +66,19 @@ public: return new TestFlow(flowStopCount, flowResumeCount, flowStopSize, flowResumeSize); } - static QueueFlowLimit *getQueueFlowLimit(const qpid::framing::FieldTable& settings) + static QueueFlowLimit *getQueueFlowLimit(const qpid::framing::FieldTable& arguments) { + QueueSettings settings; + settings.populate(arguments, settings.storeSettings); return QueueFlowLimit::createLimit(0, settings); } }; - - -QueuedMessage createMessage(uint32_t size) +Message createMessage(uint32_t size) { static uint32_t seqNum; - QueuedMessage msg; - msg.payload = MessageUtils::createMessage(); - msg.position = ++seqNum; - MessageUtils::addContent(msg.payload, std::string (size, 'x')); + Message msg = MessageUtils::createMessage(qpid::types::Variant::Map(), std::string (size, 'x')); + msg.setSequence(++seqNum); return msg; } } @@ -100,7 +98,7 @@ QPID_AUTO_TEST_CASE(testFlowCount) BOOST_CHECK(!flow->isFlowControlActive()); BOOST_CHECK(flow->monitorFlowControl()); - std::deque<QueuedMessage> msgs; + std::deque<Message> msgs; for (size_t i = 0; i < 6; i++) { msgs.push_back(createMessage(10)); flow->enqueued(msgs.back()); @@ -135,7 +133,6 @@ QPID_AUTO_TEST_CASE(testFlowCount) BOOST_CHECK(!flow->isFlowControlActive()); // 4 on queue, OFF } - QPID_AUTO_TEST_CASE(testFlowSize) { FieldTable args; @@ -151,7 +148,7 @@ QPID_AUTO_TEST_CASE(testFlowSize) BOOST_CHECK(!flow->isFlowControlActive()); BOOST_CHECK(flow->monitorFlowControl()); - std::deque<QueuedMessage> msgs; + std::deque<Message> msgs; for (size_t i = 0; i < 6; i++) { msgs.push_back(createMessage(10)); flow->enqueued(msgs.back()); @@ -161,14 +158,14 @@ QPID_AUTO_TEST_CASE(testFlowSize) BOOST_CHECK_EQUAL(6u, flow->getFlowCount()); BOOST_CHECK_EQUAL(60u, flow->getFlowSize()); - QueuedMessage msg_9 = createMessage(9); + Message msg_9 = createMessage(9); flow->enqueued(msg_9); BOOST_CHECK(!flow->isFlowControlActive()); // 69 on queue - QueuedMessage tinyMsg_1 = createMessage(1); + Message tinyMsg_1 = createMessage(1); flow->enqueued(tinyMsg_1); BOOST_CHECK(!flow->isFlowControlActive()); // 70 on queue - QueuedMessage tinyMsg_2 = createMessage(1); + Message tinyMsg_2 = createMessage(1); flow->enqueued(tinyMsg_2); BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue, ON msgs.push_back(createMessage(10)); @@ -233,12 +230,12 @@ QPID_AUTO_TEST_CASE(testFlowCombo) args.setUInt64(QueueFlowLimit::flowStopSizeKey, 200); args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 100); - std::deque<QueuedMessage> msgs_1; - std::deque<QueuedMessage> msgs_10; - std::deque<QueuedMessage> msgs_50; - std::deque<QueuedMessage> msgs_100; + std::deque<Message> msgs_1; + std::deque<Message> msgs_10; + std::deque<Message> msgs_50; + std::deque<Message> msgs_100; - QueuedMessage msg; + Message msg; std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args)); BOOST_CHECK(!flow->isFlowControlActive()); // count:0 size:0 @@ -458,7 +455,6 @@ QPID_AUTO_TEST_CASE(testFlowDisable) } } - QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/cpp/src/tests/QueuePolicyTest.cpp b/cpp/src/tests/QueuePolicyTest.cpp index f735e09449..00e964602a 100644 --- a/cpp/src/tests/QueuePolicyTest.cpp +++ b/cpp/src/tests/QueuePolicyTest.cpp @@ -22,12 +22,10 @@ #include "unit_test.h" #include "test_tools.h" -#include "qpid/broker/QueuePolicy.h" #include "qpid/broker/QueueFlowLimit.h" #include "qpid/client/QueueOptions.h" #include "qpid/sys/Time.h" #include "qpid/framing/reply_exceptions.h" -#include "MessageUtils.h" #include "BrokerFixture.h" using namespace qpid::broker; @@ -39,118 +37,10 @@ namespace tests { QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite) -namespace { -QueuedMessage createMessage(uint32_t size) -{ - QueuedMessage msg; - msg.payload = MessageUtils::createMessage(); - MessageUtils::addContent(msg.payload, std::string (size, 'x')); - return msg; -} -} - -QPID_AUTO_TEST_CASE(testCount) -{ - std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 5, 0)); - BOOST_CHECK_EQUAL((uint64_t) 0, policy->getMaxSize()); - BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount()); - - QueuedMessage msg = createMessage(10); - for (size_t i = 0; i < 5; i++) { - policy->tryEnqueue(msg.payload); - } - try { - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on enqueuing sixth message"); - } catch (const ResourceLimitExceededException&) {} - - policy->dequeued(msg); - policy->tryEnqueue(msg.payload); - - try { - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)"); - } catch (const ResourceLimitExceededException&) {} -} - -QPID_AUTO_TEST_CASE(testSize) -{ - std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 0, 50)); - QueuedMessage msg = createMessage(10); - - for (size_t i = 0; i < 5; i++) { - policy->tryEnqueue(msg.payload); - } - try { - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); - } catch (const ResourceLimitExceededException&) {} - - policy->dequeued(msg); - policy->tryEnqueue(msg.payload); - - try { - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy); - } catch (const ResourceLimitExceededException&) {} -} - -QPID_AUTO_TEST_CASE(testBoth) -{ - std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 5, 50)); - try { - QueuedMessage msg = createMessage(51); - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on single message exceeding 50. " << *policy); - } catch (const ResourceLimitExceededException&) {} - - std::vector<QueuedMessage> messages; - messages.push_back(createMessage(15)); - messages.push_back(createMessage(10)); - messages.push_back(createMessage(11)); - messages.push_back(createMessage(2)); - messages.push_back(createMessage(7)); - for (size_t i = 0; i < messages.size(); i++) { - policy->tryEnqueue(messages[i].payload); - } - //size = 45 at this point, count = 5 - try { - QueuedMessage msg = createMessage(5); - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on count exceeding 6. " << *policy); - } catch (const ResourceLimitExceededException&) {} - try { - QueuedMessage msg = createMessage(10); - policy->tryEnqueue(msg.payload); - BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); - } catch (const ResourceLimitExceededException&) {} - - - policy->dequeued(messages[0]); - try { - QueuedMessage msg = createMessage(20); - policy->tryEnqueue(msg.payload); - } catch (const ResourceLimitExceededException&) { - BOOST_FAIL("Policy failed incorrectly after dequeue. " << *policy); - } -} - -QPID_AUTO_TEST_CASE(testSettings) -{ - //test reading and writing the policy from/to field table - std::auto_ptr<QueuePolicy> a(QueuePolicy::createQueuePolicy("test", 101, 303)); - FieldTable settings; - a->update(settings); - std::auto_ptr<QueuePolicy> b(QueuePolicy::createQueuePolicy("test", settings)); - BOOST_CHECK_EQUAL(a->getMaxCount(), b->getMaxCount()); - BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize()); -} - QPID_AUTO_TEST_CASE(testRingPolicyCount) { - FieldTable args; - std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING); - policy->update(args); + QueueOptions args; + args.setSizePolicy(RING, 0, 5); SessionFixture f; std::string q("my-ring-queue"); @@ -183,9 +73,8 @@ QPID_AUTO_TEST_CASE(testRingPolicySize) // Ring queue, 500 bytes maxSize - FieldTable args; - std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 0, 500, QueuePolicy::RING); - policy->update(args); + QueueOptions args; + args.setSizePolicy(RING, 500, 0); SessionFixture f; std::string q("my-ring-queue"); @@ -255,9 +144,9 @@ QPID_AUTO_TEST_CASE(testRingPolicySize) QPID_AUTO_TEST_CASE(testStrictRingPolicy) { - FieldTable args; - std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING_STRICT); - policy->update(args); + QueueOptions args; + args.setSizePolicy(RING_STRICT, 0, 5); + args.setString("qpid.flow_stop_count", "0"); SessionFixture f; std::string q("my-ring-queue"); @@ -281,9 +170,8 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy) QPID_AUTO_TEST_CASE(testPolicyWithDtx) { - FieldTable args; - std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT); - policy->update(args); + QueueOptions args; + args.setSizePolicy(REJECT, 0, 5); SessionFixture f; std::string q("my-policy-queue"); @@ -367,9 +255,8 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore) QPID_AUTO_TEST_CASE(testPolicyFailureOnCommit) { - FieldTable args; - std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT); - policy->update(args); + QueueOptions args; + args.setSizePolicy(REJECT, 0, 5); SessionFixture f; std::string q("q"); diff --git a/cpp/src/tests/QueueRegistryTest.cpp b/cpp/src/tests/QueueRegistryTest.cpp index ae555539a4..364d66c525 100644 --- a/cpp/src/tests/QueueRegistryTest.cpp +++ b/cpp/src/tests/QueueRegistryTest.cpp @@ -19,6 +19,7 @@ #include "qpid/broker/QueueRegistry.h" #include "qpid/broker/Queue.h" +#include "qpid/broker/QueueSettings.h" #include "unit_test.h" #include <string> @@ -36,33 +37,23 @@ QPID_AUTO_TEST_CASE(testDeclare) QueueRegistry reg; std::pair<Queue::shared_ptr, bool> qc; - qc = reg.declare(foo, false, 0, 0); + qc = reg.declare(foo, QueueSettings()); Queue::shared_ptr q = qc.first; BOOST_CHECK(q); BOOST_CHECK(qc.second); // New queue BOOST_CHECK_EQUAL(foo, q->getName()); - qc = reg.declare(foo, false, 0, 0); + qc = reg.declare(foo, QueueSettings()); BOOST_CHECK_EQUAL(q, qc.first); BOOST_CHECK(!qc.second); - qc = reg.declare(bar, false, 0, 0); + qc = reg.declare(bar, QueueSettings()); q = qc.first; BOOST_CHECK(q); BOOST_CHECK_EQUAL(true, qc.second); BOOST_CHECK_EQUAL(bar, q->getName()); } -QPID_AUTO_TEST_CASE(testDeclareTmp) -{ - QueueRegistry reg; - std::pair<Queue::shared_ptr, bool> qc; - - qc = reg.declare(std::string(), false, 0, 0); - BOOST_CHECK(qc.second); - BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName()); -} - QPID_AUTO_TEST_CASE(testFind) { std::string foo("foo"); @@ -72,8 +63,8 @@ QPID_AUTO_TEST_CASE(testFind) BOOST_CHECK(reg.find(foo) == 0); - reg.declare(foo, false, 0, 0); - reg.declare(bar, false, 0, 0); + reg.declare(foo, QueueSettings()); + reg.declare(bar, QueueSettings()); Queue::shared_ptr q = reg.find(bar); BOOST_CHECK(q); BOOST_CHECK_EQUAL(bar, q->getName()); @@ -85,7 +76,7 @@ QPID_AUTO_TEST_CASE(testDestroy) QueueRegistry reg; std::pair<Queue::shared_ptr, bool> qc; - qc = reg.declare(foo, false, 0, 0); + qc = reg.declare(foo, QueueSettings()); reg.destroy(foo); // Queue is gone from the registry. BOOST_CHECK(reg.find(foo) == 0); diff --git a/cpp/src/tests/QueueTest.cpp b/cpp/src/tests/QueueTest.cpp index 3b4f74620f..3dfe3863f4 100644 --- a/cpp/src/tests/QueueTest.cpp +++ b/cpp/src/tests/QueueTest.cpp @@ -38,8 +38,8 @@ #include "qpid/framing/AMQFrame.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/reply_exceptions.h" -#include "qpid/broker/QueuePolicy.h" #include "qpid/broker/QueueFlowLimit.h" +#include "qpid/broker/QueueSettings.h" #include <iostream> #include <vector> @@ -56,196 +56,47 @@ using namespace qpid::sys; namespace qpid { namespace tests { - class TestConsumer : public virtual Consumer{ public: typedef boost::shared_ptr<TestConsumer> shared_ptr; - QueuedMessage last; + QueueCursor lastCursor; + Message lastMessage; bool received; - TestConsumer(std::string name="test", bool acquire = true):Consumer(name, acquire), received(false) {}; + TestConsumer(std::string name="test", bool acquire = true) : Consumer(name, acquire ? CONSUMER : BROWSER), received(false) {}; - virtual bool deliver(QueuedMessage& msg){ - last = msg; + virtual bool deliver(const QueueCursor& cursor, const Message& message){ + lastCursor = cursor; + lastMessage = message; received = true; return true; }; void notify() {} void cancel() {} - void acknowledged(const QueuedMessage&) {} + void acknowledged(const DeliveryRecord&) {} OwnershipToken* getSession() { return 0; } }; class FailOnDeliver : public Deliverable { - boost::intrusive_ptr<Message> msg; + Message msg; public: FailOnDeliver() : msg(MessageUtils::createMessage()) {} void deliverTo(const boost::shared_ptr<Queue>& queue) { throw Exception(QPID_MSG("Invalid delivery to " << queue->getName())); } - Message& getMessage() { return *(msg.get()); } + Message& getMessage() { return msg; } }; -intrusive_ptr<Message> createMessage(std::string exchange, std::string routingKey, uint64_t ttl = 0) { - intrusive_ptr<Message> msg(new Message()); - AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); - AMQFrame header((AMQHeaderBody())); - msg->getFrames().append(method); - msg->getFrames().append(header); - msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); - if (ttl) msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setTtl(ttl); - return msg; -} - -intrusive_ptr<Message> contentMessage(string content) { - intrusive_ptr<Message> m(MessageUtils::createMessage()); - MessageUtils::addContent(m, content); - return m; -} - -string getContent(intrusive_ptr<Message> m) { - return m->getFrames().getContent(); -} - QPID_AUTO_TEST_SUITE(QueueTestSuite) -QPID_AUTO_TEST_CASE(testAsyncMessage) { - Queue::shared_ptr queue(new Queue("my_test_queue", true)); - intrusive_ptr<Message> received; - - TestConsumer::shared_ptr c1(new TestConsumer()); - queue->consume(c1); - - - //Test basic delivery: - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - msg1->enqueueAsync(queue, (MessageStore*)0);//this is done on enqueue which is not called from process - queue->process(msg1); - sleep(2); - - BOOST_CHECK(!c1->received); - msg1->enqueueComplete(); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg1.get(), received.get()); -} - - -QPID_AUTO_TEST_CASE(testAsyncMessageCount){ - Queue::shared_ptr queue(new Queue("my_test_queue", true)); - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - msg1->enqueueAsync(queue, (MessageStore*)0);//this is done on enqueue which is not called from process - - queue->process(msg1); - sleep(2); - uint32_t compval=0; - BOOST_CHECK_EQUAL(compval, queue->getEnqueueCompleteMessageCount()); - msg1->enqueueComplete(); - compval=1; - BOOST_CHECK_EQUAL(compval, queue->getEnqueueCompleteMessageCount()); - BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); -} - -QPID_AUTO_TEST_CASE(testConsumers){ - Queue::shared_ptr queue(new Queue("my_queue", true)); - - //Test adding consumers: - TestConsumer::shared_ptr c1(new TestConsumer()); - TestConsumer::shared_ptr c2(new TestConsumer()); - queue->consume(c1); - queue->consume(c2); - - BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount()); - - //Test basic delivery: - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - - queue->deliver(msg1); - BOOST_CHECK(queue->dispatch(c1)); - BOOST_CHECK_EQUAL(msg1.get(), c1->last.payload.get()); - - queue->deliver(msg2); - BOOST_CHECK(queue->dispatch(c2)); - BOOST_CHECK_EQUAL(msg2.get(), c2->last.payload.get()); - - c1->received = false; - queue->deliver(msg3); - BOOST_CHECK(queue->dispatch(c1)); - BOOST_CHECK_EQUAL(msg3.get(), c1->last.payload.get()); - - //Test cancellation: - queue->cancel(c1); - BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount()); - queue->cancel(c2); - BOOST_CHECK_EQUAL(uint32_t(0), queue->getConsumerCount()); -} - -QPID_AUTO_TEST_CASE(testRegistry){ - //Test use of queues in registry: - QueueRegistry registry; - registry.declare("queue1", true, true); - registry.declare("queue2", true, true); - registry.declare("queue3", true, true); - - BOOST_CHECK(registry.find("queue1")); - BOOST_CHECK(registry.find("queue2")); - BOOST_CHECK(registry.find("queue3")); - - registry.destroy("queue1"); - registry.destroy("queue2"); - registry.destroy("queue3"); - - BOOST_CHECK(!registry.find("queue1")); - BOOST_CHECK(!registry.find("queue2")); - BOOST_CHECK(!registry.find("queue3")); -} - -QPID_AUTO_TEST_CASE(testDequeue){ - Queue::shared_ptr queue(new Queue("my_queue", true)); - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - intrusive_ptr<Message> received; - - queue->deliver(msg1); - queue->deliver(msg2); - queue->deliver(msg3); - - BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount()); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg1.get(), received.get()); - BOOST_CHECK_EQUAL(uint32_t(2), queue->getMessageCount()); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg2.get(), received.get()); - BOOST_CHECK_EQUAL(uint32_t(1), queue->getMessageCount()); - - TestConsumer::shared_ptr consumer(new TestConsumer()); - queue->consume(consumer); - queue->dispatch(consumer); - if (!consumer->received) - sleep(2); - - BOOST_CHECK_EQUAL(msg3.get(), consumer->last.payload.get()); - BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount()); - - received = queue->get().payload; - BOOST_CHECK(!received); - BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount()); - -} - QPID_AUTO_TEST_CASE(testBound){ //test the recording of bindings, and use of those to allow a queue to be unbound string key("my-key"); FieldTable args; - Queue::shared_ptr queue(new Queue("my-queue", true)); + Queue::shared_ptr queue(new Queue("my-queue")); ExchangeRegistry exchanges; //establish bindings from exchange->queue and notify the queue as it is bound: Exchange::shared_ptr exchange1 = exchanges.declare("my-exchange-1", "direct").first; @@ -273,423 +124,69 @@ QPID_AUTO_TEST_CASE(testBound){ exchange3->route(deliverable); } -QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){ - client::QueueOptions args; - args.setPersistLastNode(); - - Queue::shared_ptr queue(new Queue("my-queue", true)); - queue->configure(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - - //enqueue 2 messages - queue->deliver(msg1); - queue->deliver(msg2); - - //change mode - queue->setLastNodeFailure(); - - //enqueue 1 message - queue->deliver(msg3); - - //check all have persistent ids. - BOOST_CHECK(msg1->isPersistent()); - BOOST_CHECK(msg2->isPersistent()); - BOOST_CHECK(msg3->isPersistent()); - -} - - -QPID_AUTO_TEST_CASE(testSeek){ - - Queue::shared_ptr queue(new Queue("my-queue", true)); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - - //enqueue 2 messages - queue->deliver(msg1); - queue->deliver(msg2); - queue->deliver(msg3); - - TestConsumer::shared_ptr consumer(new TestConsumer("test", false)); - SequenceNumber seq(2); - consumer->setPosition(seq); - - QueuedMessage qm; - queue->dispatch(consumer); - - BOOST_CHECK_EQUAL(msg3.get(), consumer->last.payload.get()); - queue->dispatch(consumer); - queue->dispatch(consumer); // make sure over-run is safe - -} - -QPID_AUTO_TEST_CASE(testSearch){ - - Queue::shared_ptr queue(new Queue("my-queue", true)); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - - //enqueue 2 messages - queue->deliver(msg1); - queue->deliver(msg2); - queue->deliver(msg3); - - SequenceNumber seq(2); - QueuedMessage qm; - TestConsumer::shared_ptr c1(new TestConsumer()); - - BOOST_CHECK(queue->find(seq, qm)); +QPID_AUTO_TEST_CASE(testLVQ){ - BOOST_CHECK_EQUAL(seq.getValue(), qm.position.getValue()); - - queue->acquire(qm, c1->getName()); - BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - SequenceNumber seq1(3); - QueuedMessage qm1; - BOOST_CHECK(queue->find(seq1, qm1)); - BOOST_CHECK_EQUAL(seq1.getValue(), qm1.position.getValue()); - -} -const std::string nullxid = ""; - -class SimpleDummyCtxt : public TransactionContext {}; + QueueSettings settings; + string key="key"; + settings.lvqKey = key; + QueueFactory factory; + Queue::shared_ptr q(factory.create("my-queue", settings)); -class DummyCtxt : public TPCTransactionContext -{ - const std::string xid; - public: - DummyCtxt(const std::string& _xid) : xid(_xid) {} - static std::string getXid(TransactionContext& ctxt) - { - DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt)); - return c ? c->xid : nullxid; + const char* values[] = { "a", "b", "c", "a"}; + for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); ++i) { + qpid::types::Variant::Map properties; + properties[key] = values[i]; + q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+1))); } -}; - -class TestMessageStoreOC : public MessageStore -{ - std::set<std::string> prepared; - uint64_t nextPersistenceId; - public: - - uint enqCnt; - uint deqCnt; - bool error; - - TestMessageStoreOC() : MessageStore(),nextPersistenceId(1),enqCnt(0),deqCnt(0),error(false) {} - ~TestMessageStoreOC(){} + BOOST_CHECK_EQUAL(q->getMessageCount(), 3u); - virtual void dequeue(TransactionContext*, - const boost::intrusive_ptr<PersistableMessage>& /*msg*/, - const PersistableQueue& /*queue*/) - { - if (error) throw Exception("Dequeue error test"); - deqCnt++; - } + TestConsumer::shared_ptr c(new TestConsumer("test", true)); + BOOST_CHECK(q->dispatch(c)); + BOOST_CHECK_EQUAL(std::string("2"), c->lastMessage.getContent()); + BOOST_CHECK(q->dispatch(c)); + BOOST_CHECK_EQUAL(std::string("3"), c->lastMessage.getContent()); + BOOST_CHECK(q->dispatch(c)); + BOOST_CHECK_EQUAL(std::string("4"), c->lastMessage.getContent()); - virtual void enqueue(TransactionContext*, - const boost::intrusive_ptr<PersistableMessage>& msg, - const PersistableQueue& /* queue */) - { - if (error) throw Exception("Enqueue error test"); - enqCnt++; - msg->enqueueComplete(); - } - void createError() - { - error=true; + const char* values2[] = { "a", "b", "c"}; + for (size_t i = 0; i < sizeof(values2)/sizeof(values2[0]); ++i) { + qpid::types::Variant::Map properties; + properties[key] = values[i]; + q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+5))); } + BOOST_CHECK_EQUAL(q->getMessageCount(), 3u); - bool init(const Options*) { return true; } - void truncateInit(const bool) {} - void create(PersistableQueue& queue, const framing::FieldTable&) { queue.setPersistenceId(nextPersistenceId++); } - void destroy(PersistableQueue&) {} - void create(const PersistableExchange& exchange, const framing::FieldTable&) { exchange.setPersistenceId(nextPersistenceId++); } - void destroy(const PersistableExchange&) {} - void bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {} - void unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {} - void create(const PersistableConfig& config) { config.setPersistenceId(nextPersistenceId++); } - void destroy(const PersistableConfig&) {} - void stage(const boost::intrusive_ptr<PersistableMessage>&) {} - void destroy(PersistableMessage&) {} - void appendContent(const boost::intrusive_ptr<const PersistableMessage>&, const std::string&) {} - void loadContent(const qpid::broker::PersistableQueue&, const boost::intrusive_ptr<const PersistableMessage>&, - std::string&, uint64_t, uint32_t) { throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled"); } - void flush(const qpid::broker::PersistableQueue&) {} - uint32_t outstandingQueueAIO(const PersistableQueue&) { return 0; } - - std::auto_ptr<TransactionContext> begin() { return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt()); } - std::auto_ptr<TPCTransactionContext> begin(const std::string& xid) { return std::auto_ptr<TPCTransactionContext>(new DummyCtxt(xid)); } - void prepare(TPCTransactionContext& ctxt) { prepared.insert(DummyCtxt::getXid(ctxt)); } - void commit(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); } - void abort(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); } - void collectPreparedXids(std::set<std::string>& out) { out.insert(prepared.begin(), prepared.end()); } - - void recover(RecoveryManager&) {} -}; - - -QPID_AUTO_TEST_CASE(testLVQOrdering){ - - client::QueueOptions args; - // set queue mode - args.setOrdering(client::LVQ); - - Queue::shared_ptr queue(new Queue("my-queue", true )); - queue->configure(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - intrusive_ptr<Message> msg4 = createMessage("e", "D"); - intrusive_ptr<Message> received; - - //set deliever match for LVQ a,b,c,a - - string key; - args.getLVQKey(key); - BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - - - msg1->insertCustomProperty(key,"a"); - msg2->insertCustomProperty(key,"b"); - msg3->insertCustomProperty(key,"c"); - msg4->insertCustomProperty(key,"a"); - - //enqueue 4 message - queue->deliver(msg1); - queue->deliver(msg2); - queue->deliver(msg3); - queue->deliver(msg4); - - BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg4.get(), received.get()); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg2.get(), received.get()); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg3.get(), received.get()); - - intrusive_ptr<Message> msg5 = createMessage("e", "A"); - intrusive_ptr<Message> msg6 = createMessage("e", "B"); - intrusive_ptr<Message> msg7 = createMessage("e", "C"); - msg5->insertCustomProperty(key,"a"); - msg6->insertCustomProperty(key,"b"); - msg7->insertCustomProperty(key,"c"); - queue->deliver(msg5); - queue->deliver(msg6); - queue->deliver(msg7); - - BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg5.get(), received.get()); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg6.get(), received.get()); - - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg7.get(), received.get()); - + BOOST_CHECK(q->dispatch(c)); + BOOST_CHECK_EQUAL(std::string("5"), c->lastMessage.getContent()); + BOOST_CHECK(q->dispatch(c)); + BOOST_CHECK_EQUAL(std::string("6"), c->lastMessage.getContent()); + BOOST_CHECK(q->dispatch(c)); + BOOST_CHECK_EQUAL(std::string("7"), c->lastMessage.getContent()); } QPID_AUTO_TEST_CASE(testLVQEmptyKey){ - client::QueueOptions args; - // set queue mode - args.setOrdering(client::LVQ); - - Queue::shared_ptr queue(new Queue("my-queue", true )); - queue->configure(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - - string key; - args.getLVQKey(key); - BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - - - msg1->insertCustomProperty(key,"a"); - queue->deliver(msg1); - queue->deliver(msg2); - BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - -} - -QPID_AUTO_TEST_CASE(testLVQAcquire){ - - client::QueueOptions args; - // set queue mode - args.setOrdering(client::LVQ); - // disable flow control, as this test violates the enqueue/dequeue sequence. - args.setInt(QueueFlowLimit::flowStopCountKey, 0); - - Queue::shared_ptr queue(new Queue("my-queue", true )); - queue->configure(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - intrusive_ptr<Message> msg3 = createMessage("e", "C"); - intrusive_ptr<Message> msg4 = createMessage("e", "D"); - intrusive_ptr<Message> msg5 = createMessage("e", "F"); - intrusive_ptr<Message> msg6 = createMessage("e", "G"); - - //set deliever match for LVQ a,b,c,a - - string key; - args.getLVQKey(key); - BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - - - msg1->insertCustomProperty(key,"a"); - msg2->insertCustomProperty(key,"b"); - msg3->insertCustomProperty(key,"c"); - msg4->insertCustomProperty(key,"a"); - msg5->insertCustomProperty(key,"b"); - msg6->insertCustomProperty(key,"c"); - - //enqueue 4 message - queue->deliver(msg1); - queue->deliver(msg2); - queue->deliver(msg3); - queue->deliver(msg4); - - BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - - framing::SequenceNumber sequence(1); - QueuedMessage qmsg(queue.get(), msg1, sequence); - QueuedMessage qmsg2(queue.get(), msg2, ++sequence); - framing::SequenceNumber sequence1(10); - QueuedMessage qmsg3(queue.get(), 0, sequence1); - TestConsumer::shared_ptr dummy(new TestConsumer()); - - BOOST_CHECK(!queue->acquire(qmsg, dummy->getName())); - BOOST_CHECK(queue->acquire(qmsg2, dummy->getName())); - // Acquire the massage again to test failure case. - BOOST_CHECK(!queue->acquire(qmsg2, dummy->getName())); - BOOST_CHECK(!queue->acquire(qmsg3, dummy->getName())); - - BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u); - - queue->deliver(msg5); - BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - - // set mode to no browse and check - args.setOrdering(client::LVQ_NO_BROWSE); - queue->configure(args); - TestConsumer::shared_ptr c1(new TestConsumer("test", false)); - - queue->dispatch(c1); - queue->dispatch(c1); - queue->dispatch(c1); - - queue->deliver(msg6); - BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u); - - intrusive_ptr<Message> received; - received = queue->get().payload; - BOOST_CHECK_EQUAL(msg4.get(), received.get()); - -} - -QPID_AUTO_TEST_CASE(testLVQMultiQueue){ - - client::QueueOptions args; - // set queue mode - args.setOrdering(client::LVQ); - - Queue::shared_ptr queue1(new Queue("my-queue", true )); - Queue::shared_ptr queue2(new Queue("my-queue", true )); - intrusive_ptr<Message> received; - queue1->configure(args); - queue2->configure(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "A"); - - string key; - args.getLVQKey(key); - BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - - msg1->insertCustomProperty(key,"a"); - msg2->insertCustomProperty(key,"a"); - - queue1->deliver(msg1); - queue2->deliver(msg1); - queue1->deliver(msg2); - - received = queue1->get().payload; - BOOST_CHECK_EQUAL(msg2.get(), received.get()); - - received = queue2->get().payload; - BOOST_CHECK_EQUAL(msg1.get(), received.get()); + QueueSettings settings; + string key="key"; + settings.lvqKey = key; + QueueFactory factory; + Queue::shared_ptr q(factory.create("my-queue", settings)); -} -QPID_AUTO_TEST_CASE(testLVQRecover){ - -/* simulate this - 1. start 2 nodes - 2. create cluster durable lvq - 3. send a transient message to the queue - 4. kill one of the nodes (to trigger force persistent behaviour)... - 5. then restart it (to turn off force persistent behaviour) - 6. send another transient message with same lvq key as in 3 - 7. kill the second node again (retrigger force persistent) - 8. stop and recover the first node -*/ - TestMessageStoreOC testStore; - client::QueueOptions args; - // set queue mode - args.setOrdering(client::LVQ); - args.setPersistLastNode(); - - Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); - intrusive_ptr<Message> received; - queue1->create(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - intrusive_ptr<Message> msg2 = createMessage("e", "A"); - // 2 - string key; - args.getLVQKey(key); - BOOST_CHECK_EQUAL(key, "qpid.LVQ_key"); - - msg1->insertCustomProperty(key,"a"); - msg2->insertCustomProperty(key,"a"); - // 3 - queue1->deliver(msg1); - // 4 - queue1->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 1u); - // 5 - queue1->clearLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 1u); - // 6 - queue1->deliver(msg2); - BOOST_CHECK_EQUAL(testStore.enqCnt, 1u); - queue1->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 2u); - BOOST_CHECK_EQUAL(testStore.deqCnt, 1u); + qpid::types::Variant::Map properties; + properties["key"] = "a"; + q->deliver(MessageUtils::createMessage(properties, "one")); + properties.clear(); + q->deliver(MessageUtils::createMessage(properties, "two")); + BOOST_CHECK_EQUAL(q->getMessageCount(), 2u); } void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0) { for (uint i = 0; i < count; i++) { - intrusive_ptr<Message> m = createMessage("exchange", "key", i % 2 ? oddTtl : evenTtl); - m->computeExpiration(new broker::ExpiryPolicy); + Message m = MessageUtils::createMessage("exchange", "key", i % 2 ? oddTtl : evenTtl); + m.computeExpiration(new broker::ExpiryPolicy); queue.deliver(m); } } @@ -706,7 +203,7 @@ QPID_AUTO_TEST_CASE(testPurgeExpired) { QPID_AUTO_TEST_CASE(testQueueCleaner) { Timer timer; QueueRegistry queues; - Queue::shared_ptr queue = queues.declare("my-queue").first; + Queue::shared_ptr queue = queues.declare("my-queue", QueueSettings()).first; addMessagesToQueue(10, *queue, 200, 400); BOOST_CHECK_EQUAL(queue->getMessageCount(), 10u); @@ -717,44 +214,57 @@ QPID_AUTO_TEST_CASE(testQueueCleaner) { ::usleep(300*1000); BOOST_CHECK_EQUAL(queue->getMessageCount(), 0u); } - - namespace { - // helper for group tests - void verifyAcquire( Queue::shared_ptr queue, - TestConsumer::shared_ptr c, - std::deque<QueuedMessage>& results, - const std::string& expectedGroup, - const int expectedId ) - { - BOOST_CHECK(queue->dispatch(c)); - results.push_back(c->last); - std::string group = c->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsString("GROUP-ID"); - int id = c->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID"); +int getIntProperty(const Message& message, const std::string& key) +{ + qpid::types::Variant v = message.getProperty(key); + int i(0); + if (!v.isVoid()) i = v; + return i; +} +// helper for group tests +void verifyAcquire( Queue::shared_ptr queue, + TestConsumer::shared_ptr c, + std::deque<QueueCursor>& results, + const std::string& expectedGroup, + const int expectedId ) +{ + bool success = queue->dispatch(c); + BOOST_CHECK(success); + if (success) { + results.push_back(c->lastCursor); + std::string group = c->lastMessage.getPropertyAsString("GROUP-ID"); + int id = getIntProperty(c->lastMessage, "MY-ID"); BOOST_CHECK_EQUAL( group, expectedGroup ); BOOST_CHECK_EQUAL( id, expectedId ); } } +Message createGroupMessage(int id, const std::string& group) +{ + qpid::types::Variant::Map properties; + properties["GROUP-ID"] = group; + properties["MY-ID"] = id; + return MessageUtils::createMessage(properties); +} +} + QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { // // Verify that consumers of grouped messages own the groups once a message is acquired, // and release the groups once all acquired messages have been dequeued or requeued // - FieldTable args; - Queue::shared_ptr queue(new Queue("my_queue", true)); - args.setString("qpid.group_header_key", "GROUP-ID"); - args.setInt("qpid.shared_msg_group", 1); - queue->configure(args); + QueueSettings settings; + settings.shareGroups = 1; + settings.groupKey = "GROUP-ID"; + QueueFactory factory; + Queue::shared_ptr queue(factory.create("my_queue", settings)); std::string groups[] = { std::string("a"), std::string("a"), std::string("a"), std::string("b"), std::string("b"), std::string("b"), std::string("c"), std::string("c"), std::string("c") }; for (int i = 0; i < 9; ++i) { - intrusive_ptr<Message> msg = createMessage("e", "A"); - msg->insertCustomProperty("GROUP-ID", groups[i]); - msg->insertCustomProperty("MY-ID", i); - queue->deliver(msg); + queue->deliver(createGroupMessage(i, groups[i])); } // Queue = a-0, a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8... @@ -768,8 +278,8 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { queue->consume(c1); queue->consume(c2); - std::deque<QueuedMessage> dequeMeC1; - std::deque<QueuedMessage> dequeMeC2; + std::deque<QueueCursor> dequeMeC1; + std::deque<QueueCursor> dequeMeC2; verifyAcquire(queue, c1, dequeMeC1, "a", 0 ); // c1 now owns group "a" (acquire a-0) @@ -828,9 +338,9 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { // Owners= ^C2, ^C2, ^C1, ^C1, ^C2, ^C2 // what happens if C-2 "requeues" a-1 and a-2? - queue->requeue( dequeMeC2.front() ); + queue->release( dequeMeC2.front() ); dequeMeC2.pop_front(); - queue->requeue( dequeMeC2.front() ); + queue->release( dequeMeC2.front() ); dequeMeC2.pop_front(); // now just has c-7 acquired // Queue = a-1, a-2, b-4, b-5, c-7, c-8... @@ -855,9 +365,9 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { gotOne = queue->dispatch(c2); BOOST_CHECK( !gotOne ); - // requeue all of C1's acquired messages, then cancel C1 + // release all of C1's acquired messages, then cancel C1 while (!dequeMeC1.empty()) { - queue->requeue(dequeMeC1.front()); + queue->release(dequeMeC1.front()); dequeMeC1.pop_front(); } queue->cancel(c1); @@ -877,7 +387,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { // Owners= ---, ---, --- TestConsumer::shared_ptr c3(new TestConsumer("C3")); - std::deque<QueuedMessage> dequeMeC3; + std::deque<QueueCursor> dequeMeC3; verifyAcquire(queue, c3, dequeMeC3, "a", 2 ); verifyAcquire(queue, c2, dequeMeC2, "b", 4 ); @@ -897,11 +407,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { // Queue = a-2, // Owners= ^C3, - - intrusive_ptr<Message> msg = createMessage("e", "A"); - msg->insertCustomProperty("GROUP-ID", "a"); - msg->insertCustomProperty("MY-ID", 9); - queue->deliver(msg); + queue->deliver(createGroupMessage(9, "a")); // Queue = a-2, a-9 // Owners= ^C3, ^C3 @@ -909,10 +415,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) { gotOne = queue->dispatch(c2); BOOST_CHECK( !gotOne ); - msg = createMessage("e", "A"); - msg->insertCustomProperty("GROUP-ID", "b"); - msg->insertCustomProperty("MY-ID", 10); - queue->deliver(msg); + queue->deliver(createGroupMessage(10, "b")); // Queue = a-2, a-9, b-10 // Owners= ^C3, ^C3, ---- @@ -933,17 +436,17 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) { // Verify that the same default group name is automatically applied to messages that // do not specify a group name. // - FieldTable args; - Queue::shared_ptr queue(new Queue("my_queue", true)); - args.setString("qpid.group_header_key", "GROUP-ID"); - args.setInt("qpid.shared_msg_group", 1); - queue->configure(args); + QueueSettings settings; + settings.shareGroups = 1; + settings.groupKey = "GROUP-ID"; + QueueFactory factory; + Queue::shared_ptr queue(factory.create("my_queue", settings)); for (int i = 0; i < 3; ++i) { - intrusive_ptr<Message> msg = createMessage("e", "A"); + qpid::types::Variant::Map properties; // no "GROUP-ID" header - msg->insertCustomProperty("MY-ID", i); - queue->deliver(msg); + properties["MY-ID"] = i; + queue->deliver(MessageUtils::createMessage(properties)); } // Queue = 0, 1, 2 @@ -956,20 +459,20 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) { queue->consume(c1); queue->consume(c2); - std::deque<QueuedMessage> dequeMeC1; - std::deque<QueuedMessage> dequeMeC2; + std::deque<QueueCursor> dequeMeC1; + std::deque<QueueCursor> dequeMeC2; queue->dispatch(c1); // c1 now owns default group (acquired 0) - dequeMeC1.push_back(c1->last); - int id = c1->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID"); + dequeMeC1.push_back(c1->lastCursor); + int id = getIntProperty(c1->lastMessage, "MY-ID"); BOOST_CHECK_EQUAL( id, 0 ); bool gotOne = queue->dispatch(c2); // c2 should get nothing BOOST_CHECK( !gotOne ); queue->dispatch(c1); // c1 now acquires 1 - dequeMeC1.push_back(c1->last); - id = c1->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID"); + dequeMeC1.push_back(c1->lastCursor); + id = getIntProperty(c1->lastMessage, "MY-ID"); BOOST_CHECK_EQUAL( id, 1 ); gotOne = queue->dispatch(c2); // c2 should still get nothing @@ -982,7 +485,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) { // now default group should be available... queue->dispatch(c2); // c2 now owns default group (acquired 2) - id = c2->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID"); + id = c2->lastMessage.getProperty("MY-ID"); BOOST_CHECK_EQUAL( id, 2 ); gotOne = queue->dispatch(c1); // c1 should get nothing @@ -992,556 +495,128 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) { queue->cancel(c2); } -QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ - - TestMessageStoreOC testStore; - client::QueueOptions args; - args.setPersistLastNode(); - - Queue::shared_ptr queue1(new Queue("queue1", true, &testStore )); - queue1->create(args); - Queue::shared_ptr queue2(new Queue("queue2", true, &testStore )); - queue2->create(args); - - intrusive_ptr<Message> msg1 = createMessage("e", "A"); - - queue1->deliver(msg1); - queue2->deliver(msg1); - - //change mode - queue1->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 1u); - queue2->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 2u); - - // check they don't get stored twice - queue1->setLastNodeFailure(); - queue2->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 2u); - - intrusive_ptr<Message> msg2 = createMessage("e", "B"); - queue1->deliver(msg2); - queue2->deliver(msg2); - - queue1->clearLastNodeFailure(); - queue2->clearLastNodeFailure(); - // check only new messages get forced - queue1->setLastNodeFailure(); - queue2->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 4u); - - // check no failure messages are stored - queue1->clearLastNodeFailure(); - queue2->clearLastNodeFailure(); - - intrusive_ptr<Message> msg3 = createMessage("e", "B"); - queue1->deliver(msg3); - queue2->deliver(msg3); - BOOST_CHECK_EQUAL(testStore.enqCnt, 4u); - queue1->setLastNodeFailure(); - queue2->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 6u); - - /** - * TODO: Fix or replace the following test which incorrectly requeues a - * message that was never on the queue in the first place. This relied on - * internal details not part of the queue abstraction. - - // check requeue 1 - intrusive_ptr<Message> msg4 = createMessage("e", "C"); - intrusive_ptr<Message> msg5 = createMessage("e", "D"); - - framing::SequenceNumber sequence(1); - QueuedMessage qmsg1(queue1.get(), msg4, sequence); - QueuedMessage qmsg2(queue2.get(), msg5, ++sequence); - - queue1->requeue(qmsg1); - BOOST_CHECK_EQUAL(testStore.enqCnt, 7u); - - // check requeue 2 - queue2->clearLastNodeFailure(); - queue2->requeue(qmsg2); - BOOST_CHECK_EQUAL(testStore.enqCnt, 7u); - queue2->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 8u); - - queue2->clearLastNodeFailure(); - queue2->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 8u); - */ -} - -QPID_AUTO_TEST_CASE(testLastNodeRecoverAndFail){ -/* -simulate this: - 1. start two nodes - 2. create cluster durable queue and add some messages - 3. kill one node (trigger force-persistent behaviour) - 4. stop and recover remaining node - 5. add another node - 6. kill that new node again -make sure that an attempt to re-enqueue a message does not happen which will -result in the last man standing exiting with an error. - -we need to make sure that recover is safe, i.e. messages are -not requeued to the store. -*/ - TestMessageStoreOC testStore; - client::QueueOptions args; - // set queue mode - args.setPersistLastNode(); - - Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); - intrusive_ptr<Message> received; - queue1->create(args); - - // check requeue 1 - intrusive_ptr<Message> msg1 = createMessage("e", "C"); - intrusive_ptr<Message> msg2 = createMessage("e", "D"); - - queue1->recover(msg1); - - queue1->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 0u); - - queue1->clearLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 0u); - - queue1->deliver(msg2); - BOOST_CHECK_EQUAL(testStore.enqCnt, 0u); - queue1->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 1u); - -} - -QPID_AUTO_TEST_CASE(testLastNodeJournalError){ -/* -simulate store exception going into last node standing - -*/ - TestMessageStoreOC testStore; - client::QueueOptions args; - // set queue mode - args.setPersistLastNode(); - - Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); - intrusive_ptr<Message> received; - queue1->configure(args); - - // check requeue 1 - intrusive_ptr<Message> msg1 = createMessage("e", "C"); - - queue1->deliver(msg1); - testStore.createError(); - - ScopedSuppressLogging sl; // Suppress messages for expected errors. - queue1->setLastNodeFailure(); - BOOST_CHECK_EQUAL(testStore.enqCnt, 0u); - -} - -intrusive_ptr<Message> mkMsg(MessageStore& store, std::string content = "", bool durable = false) -{ - intrusive_ptr<Message> msg = MessageUtils::createMessage("", "", durable); - if (content.size()) MessageUtils::addContent(msg, content); - msg->setStore(&store); - return msg; -} - -QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){ - - TestMessageStoreOC testStore; - client::QueueOptions args0; // No size policy - client::QueueOptions args1; - args1.setSizePolicy(FLOW_TO_DISK, 0, 1); - client::QueueOptions args2; - args2.setSizePolicy(FLOW_TO_DISK, 0, 2); - - // --- Fanout exchange bound to single transient queue ------------------------------------------------------------- - - FanOutExchange sbtFanout1("sbtFanout1", false, args0); // single binding to transient queue - Queue::shared_ptr tq1(new Queue("tq1", true)); // transient w/ limit - tq1->configure(args1); - sbtFanout1.bind(tq1, "", 0); - - intrusive_ptr<Message> msg01 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg01(msg01); - sbtFanout1.route(dmsg01); // Brings queue 1 to capacity limit - msg01->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg01->isContentReleased(), false); - BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); - - intrusive_ptr<Message> msg02 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg02(msg02); - { - ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg02), ResourceLimitExceededException); - } - msg02->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg02->isContentReleased(), false); - BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); - - intrusive_ptr<Message> msg03 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content - DeliverableMessage dmsg03(msg03); - { - ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg03), ResourceLimitExceededException); - } - msg03->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg03->isContentReleased(), false); - BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); - - intrusive_ptr<Message> msg04 = mkMsg(testStore); // transient no content - DeliverableMessage dmsg04(msg04); - { - ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg04), ResourceLimitExceededException); - } - msg04->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg04->isContentReleased(), false); - BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); - - intrusive_ptr<Message> msg05 = mkMsg(testStore, "", true); // durable no content - DeliverableMessage dmsg05(msg05); - { - ScopedSuppressLogging sl; // suppress expected error messages. - BOOST_CHECK_THROW(sbtFanout1.route(dmsg05), ResourceLimitExceededException); - } - msg05->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg05->isContentReleased(), false); - BOOST_CHECK_EQUAL(1u, tq1->getMessageCount()); - - // --- Fanout exchange bound to single durable queue --------------------------------------------------------------- - - FanOutExchange sbdFanout2("sbdFanout2", false, args0); // single binding to durable queue - Queue::shared_ptr dq2(new Queue("dq2", true, &testStore)); // durable w/ limit - dq2->configure(args1); - sbdFanout2.bind(dq2, "", 0); - - intrusive_ptr<Message> msg06 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg06(msg06); - 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); - 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); - 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); - 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); - msg10->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg10->isContentReleased(), true); - BOOST_CHECK_EQUAL(5u, dq2->getMessageCount()); - - // --- Fanout exchange bound to multiple durable queues ------------------------------------------------------------ - - FanOutExchange mbdFanout3("mbdFanout3", false, args0); // multiple bindings to durable queues - Queue::shared_ptr dq3(new Queue("dq3", true, &testStore)); // durable w/ limit 2 - dq3->configure(args2); - mbdFanout3.bind(dq3, "", 0); - Queue::shared_ptr dq4(new Queue("dq4", true, &testStore)); // durable w/ limit 1 - dq4->configure(args1); - mbdFanout3.bind(dq4, "", 0); - Queue::shared_ptr dq5(new Queue("dq5", true, &testStore)); // durable no limit - dq5->configure(args0); - mbdFanout3.bind(dq5, "", 0); - - intrusive_ptr<Message> msg11 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg11(msg11); - 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()); - BOOST_CHECK_EQUAL(1u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(1u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg12 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg12(msg12); - 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()); - BOOST_CHECK_EQUAL(2u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(2u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg13 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content - DeliverableMessage dmsg13(msg13); - mbdFanout3.route(dmsg13); - msg13->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg13->isContentReleased(), true); - BOOST_CHECK_EQUAL(3u, dq3->getMessageCount()); - BOOST_CHECK_EQUAL(3u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(3u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg14 = mkMsg(testStore); // transient no content - DeliverableMessage dmsg14(msg14); - 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()); - BOOST_CHECK_EQUAL(4u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(4u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg15 = mkMsg(testStore, "", true); // durable no content - DeliverableMessage dmsg15(msg15); - mbdFanout3.route(dmsg15); - msg15->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg15->isContentReleased(), true); - BOOST_CHECK_EQUAL(5u, dq3->getMessageCount()); - BOOST_CHECK_EQUAL(5u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(5u, dq5->getMessageCount()); - - // Bind a transient queue, this should block the release of any further messages. - // Note: this will result in a violation of the count policy of dq3 and dq4 - but this - // is expected until a better overall multi-queue design is implemented. Similarly - // for the other tests in this section. - - Queue::shared_ptr tq6(new Queue("tq6", true)); // transient no limit - tq6->configure(args0); - mbdFanout3.bind(tq6, "", 0); - - intrusive_ptr<Message> msg16 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg16(msg16); - mbdFanout3.route(dmsg16); - msg16->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg16->isContentReleased(), false); - BOOST_CHECK_EQUAL(6u, dq3->getMessageCount()); - BOOST_CHECK_EQUAL(6u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(6u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg17 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content - DeliverableMessage dmsg17(msg17); - mbdFanout3.route(dmsg17); - msg17->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg17->isContentReleased(), false); - BOOST_CHECK_EQUAL(7u, dq3->getMessageCount()); - BOOST_CHECK_EQUAL(7u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(7u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg18 = mkMsg(testStore); // transient no content - DeliverableMessage dmsg18(msg18); - mbdFanout3.route(dmsg18); - msg18->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg18->isContentReleased(), false); - BOOST_CHECK_EQUAL(8u, dq3->getMessageCount()); - BOOST_CHECK_EQUAL(8u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(8u, dq5->getMessageCount()); - - intrusive_ptr<Message> msg19 = mkMsg(testStore, "", true); // durable no content - DeliverableMessage dmsg19(msg19); - mbdFanout3.route(dmsg19); - msg19->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg19->isContentReleased(), false); - BOOST_CHECK_EQUAL(9u, dq3->getMessageCount()); - BOOST_CHECK_EQUAL(9u, dq4->getMessageCount()); - BOOST_CHECK_EQUAL(9u, dq5->getMessageCount()); - - - // --- Fanout exchange bound to multiple durable and transient queues ---------------------------------------------- - - FanOutExchange mbmFanout4("mbmFanout4", false, args0); // multiple bindings to durable/transient queues - Queue::shared_ptr dq7(new Queue("dq7", true, &testStore)); // durable no limit - dq7->configure(args0); - mbmFanout4.bind(dq7, "", 0); - Queue::shared_ptr dq8(new Queue("dq8", true, &testStore)); // durable w/ limit - dq8->configure(args1); - mbmFanout4.bind(dq8, "", 0); - Queue::shared_ptr tq9(new Queue("tq9", true)); // transient no limit - tq9->configure(args0); - mbmFanout4.bind(tq9, "", 0); - - intrusive_ptr<Message> msg20 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg20(msg20); - mbmFanout4.route(dmsg20); // Brings queue 7 to capacity limit - msg20->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg20->isContentReleased(), false); - BOOST_CHECK_EQUAL(1u, dq7->getMessageCount()); - BOOST_CHECK_EQUAL(1u, dq8->getMessageCount()); - BOOST_CHECK_EQUAL(1u, tq9->getMessageCount()); - - intrusive_ptr<Message> msg21 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content - DeliverableMessage dmsg21(msg21); - mbmFanout4.route(dmsg21); - msg21->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg21->isContentReleased(), false); - BOOST_CHECK_EQUAL(2u, dq7->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(2u, dq8->getMessageCount()); - BOOST_CHECK_EQUAL(2u, tq9->getMessageCount()); - - intrusive_ptr<Message> msg22 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content - DeliverableMessage dmsg22(msg22); - mbmFanout4.route(dmsg22); - msg22->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg22->isContentReleased(), false); - BOOST_CHECK_EQUAL(3u, dq7->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(3u, dq8->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(3u, tq9->getMessageCount()); - - intrusive_ptr<Message> msg23 = mkMsg(testStore); // transient no content - DeliverableMessage dmsg23(msg23); - mbmFanout4.route(dmsg23); - msg23->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg23->isContentReleased(), false); - BOOST_CHECK_EQUAL(4u, dq7->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(4u, dq8->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(4u, tq9->getMessageCount()); - - intrusive_ptr<Message> msg24 = mkMsg(testStore, "", true); // durable no content - DeliverableMessage dmsg24(msg24); - mbmFanout4.route(dmsg24); - msg24->tryReleaseContent(); - BOOST_CHECK_EQUAL(msg24->isContentReleased(), false); - BOOST_CHECK_EQUAL(5u, dq7->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(5u, dq8->getMessageCount()); // over limit - BOOST_CHECK_EQUAL(5u, tq9->getMessageCount()); -} - QPID_AUTO_TEST_CASE(testSetPositionFifo) { Queue::shared_ptr q(new Queue("my-queue", true)); BOOST_CHECK_EQUAL(q->getPosition(), SequenceNumber(0)); for (int i = 0; i < 10; ++i) - q->deliver(contentMessage(boost::lexical_cast<string>(i+1))); + q->deliver(MessageUtils::createMessage(qpid::types::Variant::Map(), boost::lexical_cast<string>(i+1))); // Verify the front of the queue TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Don't acquire BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(1u, c->last.position); // Numbered from 1 - BOOST_CHECK_EQUAL("1", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(1u, c->lastMessage.getSequence()); // Numbered from 1 + BOOST_CHECK_EQUAL("1", c->lastMessage.getContent()); + // Verify the back of the queue - QueuedMessage qm; BOOST_CHECK_EQUAL(10u, q->getPosition()); - BOOST_CHECK(q->find(q->getPosition(), qm)); // Back of the queue - BOOST_CHECK_EQUAL("10", getContent(qm.payload)); BOOST_CHECK_EQUAL(10u, q->getMessageCount()); // Using setPosition to introduce a gap in sequence numbers. q->setPosition(15); BOOST_CHECK_EQUAL(10u, q->getMessageCount()); BOOST_CHECK_EQUAL(15u, q->getPosition()); - BOOST_CHECK(q->find(10, qm)); // Back of the queue - BOOST_CHECK_EQUAL("10", getContent(qm.payload)); - q->deliver(contentMessage("16")); - c->setPosition(9); + q->deliver(MessageUtils::createMessage(qpid::types::Variant::Map(), "16")); + + q->seek(*c, Queue::MessagePredicate(), 9); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(10u, c->last.position); - BOOST_CHECK_EQUAL("10", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(10u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("10", c->lastMessage.getContent()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(16u, c->last.position); - BOOST_CHECK_EQUAL("16", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(16u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("16", c->lastMessage.getContent()); // Using setPosition to trunkcate the queue q->setPosition(5); BOOST_CHECK_EQUAL(5u, q->getMessageCount()); - q->deliver(contentMessage("6a")); - c->setPosition(4); + q->deliver(MessageUtils::createMessage(qpid::types::Variant::Map(), "6a")); + c = boost::shared_ptr<TestConsumer>(new TestConsumer("test", false)); // Don't acquire + q->seek(*c, Queue::MessagePredicate(), 4); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(5u, c->last.position); - BOOST_CHECK_EQUAL("5", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(5u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("5", c->lastMessage.getContent()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(6u, c->last.position); - BOOST_CHECK_EQUAL("6a", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(6u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("6a", c->lastMessage.getContent()); BOOST_CHECK(!q->dispatch(c)); // No more messages. } QPID_AUTO_TEST_CASE(testSetPositionLvq) { - Queue::shared_ptr q(new Queue("my-queue", true)); + QueueSettings settings; string key="key"; - framing::FieldTable args; - args.setString("qpid.last_value_queue_key", "key"); - q->configure(args); + settings.lvqKey = key; + QueueFactory factory; + Queue::shared_ptr q(factory.create("my-queue", settings)); const char* values[] = { "a", "b", "c", "a", "b", "c" }; for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); ++i) { - intrusive_ptr<Message> m = contentMessage(boost::lexical_cast<string>(i+1)); - m->insertCustomProperty(key, values[i]); - q->deliver(m); + qpid::types::Variant::Map properties; + properties[key] = values[i]; + q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+1))); } BOOST_CHECK_EQUAL(3u, q->getMessageCount()); // Verify the front of the queue TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Don't acquire BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(4u, c->last.position); // Numbered from 1 - BOOST_CHECK_EQUAL("4", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(4u, c->lastMessage.getSequence()); // Numbered from 1 + BOOST_CHECK_EQUAL("4", c->lastMessage.getContent()); // Verify the back of the queue - QueuedMessage qm; BOOST_CHECK_EQUAL(6u, q->getPosition()); - BOOST_CHECK(q->find(q->getPosition(), qm)); // Back of the queue - BOOST_CHECK_EQUAL("6", getContent(qm.payload)); q->setPosition(5); - c->setPosition(4); + + c = boost::shared_ptr<TestConsumer>(new TestConsumer("test", false)); // Don't acquire + q->seek(*c, Queue::MessagePredicate(), 4); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(5u, c->last.position); // Numbered from 1 + BOOST_CHECK_EQUAL(5u, c->lastMessage.getSequence()); // Numbered from 1 BOOST_CHECK(!q->dispatch(c)); } QPID_AUTO_TEST_CASE(testSetPositionPriority) { - Queue::shared_ptr q(new Queue("my-queue", true)); - framing::FieldTable args; - args.setInt("qpid.priorities", 10); - q->configure(args); + QueueSettings settings; + settings.priorities = 10; + QueueFactory factory; + Queue::shared_ptr q(factory.create("my-queue", settings)); const int priorities[] = { 1, 2, 3, 2, 1, 3 }; for (size_t i = 0; i < sizeof(priorities)/sizeof(priorities[0]); ++i) { - intrusive_ptr<Message> m = contentMessage(boost::lexical_cast<string>(i+1)); - m->getFrames().getHeaders()->get<DeliveryProperties>(true) - ->setPriority(priorities[i]); - q->deliver(m); + qpid::types::Variant::Map properties; + properties["priority"] = priorities[i]; + q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+1))); } // Truncation removes messages in fifo order, not priority order. q->setPosition(3); - TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Browse in FIFO order + TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Browse in priority order BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(1u, c->last.position); + BOOST_CHECK_EQUAL(3u, c->lastMessage.getSequence()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(2u, c->last.position); + BOOST_CHECK_EQUAL(2u, c->lastMessage.getSequence()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(3u, c->last.position); + BOOST_CHECK_EQUAL(1u, c->lastMessage.getSequence()); BOOST_CHECK(!q->dispatch(c)); - intrusive_ptr<Message> m = contentMessage("4a"); - m->getFrames().getHeaders()->get<DeliveryProperties>(true) - ->setPriority(4); - q->deliver(m); + qpid::types::Variant::Map properties; + properties["priority"] = 4; + q->deliver(MessageUtils::createMessage(properties, "4a")); + BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(4u, c->last.position); - BOOST_CHECK_EQUAL("4a", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(4u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("4a", c->lastMessage.getContent()); // But consumers see priority order c.reset(new TestConsumer("test", true)); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(4u, c->last.position); - BOOST_CHECK_EQUAL("4a", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(4u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("4a", c->lastMessage.getContent()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(3u, c->last.position); - BOOST_CHECK_EQUAL("3", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(3u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("3", c->lastMessage.getContent()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(2u, c->last.position); - BOOST_CHECK_EQUAL("2", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(2u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("2", c->lastMessage.getContent()); BOOST_CHECK(q->dispatch(c)); - BOOST_CHECK_EQUAL(1u, c->last.position); - BOOST_CHECK_EQUAL("1", getContent(c->last.payload)); + BOOST_CHECK_EQUAL(1u, c->lastMessage.getSequence()); + BOOST_CHECK_EQUAL("1", c->lastMessage.getContent()); } QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/ReplicationTest.cpp b/cpp/src/tests/ReplicationTest.cpp deleted file mode 100644 index 055f06579f..0000000000 --- a/cpp/src/tests/ReplicationTest.cpp +++ /dev/null @@ -1,144 +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 "unit_test.h" -#include "test_tools.h" -#include "config.h" -#include "BrokerFixture.h" - -#include "qpid/Plugin.h" -#include "qpid/broker/Broker.h" -#include "qpid/client/QueueOptions.h" -#include "qpid/framing/reply_exceptions.h" -#include "qpid/framing/SequenceNumber.h" -#include "qpid/replication/constants.h" -#include "qpid/sys/Shlib.h" - -#include <string> -#include <sstream> -#include <vector> - -#include <boost/assign.hpp> -#include <boost/bind.hpp> - -using namespace qpid::client; -using namespace qpid::framing; -using namespace qpid::replication::constants; -using boost::assign::list_of; - -namespace qpid { -namespace tests { - -QPID_AUTO_TEST_SUITE(ReplicationTestSuite) - -// FIXME aconway 2009-11-26: clean this up. -// The CMake-based build passes in the module suffix; if it's not there, this -// is a Linux/UNIX libtool-based build. -#if defined (QPID_MODULE_PREFIX) && defined (QPID_MODULE_SUFFIX) -static const char *default_shlib = - QPID_MODULE_PREFIX "replicating_listener" QPID_MODULE_POSTFIX QPID_MODULE_SUFFIX; -#else -static const char *default_shlib = ".libs/replicating_listener.so"; -#endif -qpid::sys::Shlib plugin(getLibPath("REPLICATING_LISTENER_LIB", default_shlib)); - -qpid::broker::Broker::Options getBrokerOpts(const std::vector<std::string>& args) -{ - std::vector<const char*> argv(args.size()); - transform(args.begin(), args.end(), argv.begin(), boost::bind(&std::string::c_str, _1)); - - qpid::broker::Broker::Options opts; - qpid::Plugin::addOptions(opts); - opts.parse(argv.size(), &argv[0], "", true); - return opts; -} - -QPID_AUTO_TEST_CASE(testReplicationExchange) -{ - qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<std::string>("qpidd") - ("--replication-exchange-name=qpid.replication"))); - SessionFixture f(brokerOpts); - - - std::string dataQ("queue-1"); - std::string eventQ("event-queue-1"); - std::string dataQ2("queue-2"); - std::string eventQ2("event-queue-2"); - FieldTable eventQopts; - eventQopts.setString("qpid.insert_sequence_numbers", REPLICATION_EVENT_SEQNO); - - f.session.queueDeclare(arg::queue=eventQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts); - f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ, arg::bindingKey=dataQ); - - f.session.queueDeclare(arg::queue=eventQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts); - f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ2, arg::bindingKey=dataQ2); - - QueueOptions args; - args.enableQueueEvents(false); - f.session.queueDeclare(arg::queue=dataQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); - f.session.queueDeclare(arg::queue=dataQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args); - for (int i = 0; i < 10; i++) { - f.session.messageTransfer(arg::content=Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), dataQ)); - f.session.messageTransfer(arg::content=Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), dataQ2)); - } - Message msg; - LocalQueue incoming; - Subscription sub = f.subs.subscribe(incoming, dataQ); - for (int i = 0; i < 10; i++) { - BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC)); - BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData()); - } - BOOST_CHECK(!f.subs.get(msg, dataQ)); - - sub.cancel(); - sub = f.subs.subscribe(incoming, eventQ); - //check that we received enqueue events for first queue: - for (int i = 0; i < 10; i++) { - BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC)); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), ENQUEUE); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+1)); - BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData()); - } - //check that we received dequeue events for first queue: - for (int i = 0; i < 10; i++) { - BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC)); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), DEQUEUE); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(DEQUEUED_MESSAGE_POSITION), (i+1)); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+11)); - } - - sub.cancel(); - sub = f.subs.subscribe(incoming, eventQ2); - //check that we received enqueue events for second queue: - for (int i = 0; i < 10; i++) { - BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC)); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ2); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), ENQUEUE); - BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+1)); - BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData()); - } -} - - -QPID_AUTO_TEST_SUITE_END() - -}} // namespace qpid::tests diff --git a/cpp/src/tests/TxMocks.h b/cpp/src/tests/TxMocks.h index 72cb50cd21..bf21104f70 100644 --- a/cpp/src/tests/TxMocks.h +++ b/cpp/src/tests/TxMocks.h @@ -119,8 +119,6 @@ public: assertEqualVector(expected, actual); } - void accept(TxOpConstVisitor&) const {} - ~MockTxOp(){} }; diff --git a/cpp/src/tests/TxPublishTest.cpp b/cpp/src/tests/TxPublishTest.cpp deleted file mode 100644 index a636646035..0000000000 --- a/cpp/src/tests/TxPublishTest.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 "qpid/broker/NullMessageStore.h" -#include "qpid/broker/RecoveryManager.h" -#include "qpid/broker/TxPublish.h" -#include "unit_test.h" -#include <iostream> -#include <list> -#include <vector> -#include "MessageUtils.h" -#include "TestMessageStore.h" - -using std::list; -using std::pair; -using std::vector; -using boost::intrusive_ptr; -using namespace qpid::broker; -using namespace qpid::framing; - -namespace qpid { -namespace tests { - -struct TxPublishTest -{ - - TestMessageStore store; - Queue::shared_ptr queue1; - Queue::shared_ptr queue2; - intrusive_ptr<Message> msg; - TxPublish op; - - TxPublishTest() : - queue1(new Queue("queue1", false, &store, 0)), - queue2(new Queue("queue2", false, &store, 0)), - msg(MessageUtils::createMessage("exchange", "routing_key", true)), - op(msg) - { - op.deliverTo(queue1); - op.deliverTo(queue2); - } -}; - - -QPID_AUTO_TEST_SUITE(TxPublishTestSuite) - -QPID_AUTO_TEST_CASE(testPrepare) -{ - TxPublishTest t; - - intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(t.msg); - //ensure messages are enqueued in store - t.op.prepare(0); - BOOST_CHECK_EQUAL((size_t) 2, t.store.enqueued.size()); - BOOST_CHECK_EQUAL(std::string("queue1"), t.store.enqueued[0].first); - BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[0].second); - BOOST_CHECK_EQUAL(std::string("queue2"), t.store.enqueued[1].first); - BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[1].second); - BOOST_CHECK_EQUAL( true, ( boost::static_pointer_cast<PersistableMessage>(t.msg))->isIngressComplete()); -} - -QPID_AUTO_TEST_CASE(testCommit) -{ - TxPublishTest t; - - //ensure messages are delivered to queue - t.op.prepare(0); - t.op.commit(); - BOOST_CHECK_EQUAL((uint32_t) 1, t.queue1->getMessageCount()); - intrusive_ptr<Message> msg_dequeue = t.queue1->get().payload; - - BOOST_CHECK_EQUAL( true, (boost::static_pointer_cast<PersistableMessage>(msg_dequeue))->isIngressComplete()); - BOOST_CHECK_EQUAL(t.msg, msg_dequeue); - - BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount()); - BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); -} - -QPID_AUTO_TEST_SUITE_END() - -}} // namespace qpid::tests diff --git a/cpp/src/tests/acl.py b/cpp/src/tests/acl.py index 0e096a6f5b..102796cba6 100755 --- a/cpp/src/tests/acl.py +++ b/cpp/src/tests/acl.py @@ -119,6 +119,7 @@ class ACLTests(TestBase010): def LookupPublish(self, userName, exchName, keyName, expectedResult): result = self.acl_lookupPublish(userName, exchName, keyName) if (result['result'] != expectedResult): + suffix = ', [ERROR: Expected= ' + expectedResult if (result['result'] is None): suffix = suffix + ', Exception= ' + result['text'] + ']' else: @@ -1703,6 +1704,546 @@ class ACLTests(TestBase010): result = None + #===================================== + # User name substitution + #===================================== + + def test_user_name_substitution(self): + """ + Test name substitution internals, limits, and edge cases. + """ + aclf = self.get_acl_file() + 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 all create queue name=tmp-${userdomain}\n') + aclf.write('acl allow all create queue name=${userdomain}-tmp\n') + aclf.write('acl allow all create queue name=tmp-${userdomain}-tmp\n') + aclf.write('acl allow all create queue name=tmp-${userdomain}-tmp-${userdomain}\n') + aclf.write('acl allow all create queue name=temp0-${userdomain}\n') + aclf.write('acl allow all access queue name=temp0-${userdomain}\n') + aclf.write('acl allow all purge queue name=temp0-${userdomain}\n') + aclf.write('acl allow all consume queue name=temp0-${userdomain}\n') + aclf.write('acl allow all delete queue name=temp0-${userdomain}\n') + aclf.write('acl allow all create exchange name=temp0-${userdomain}\n') + aclf.write('acl allow all access exchange name=temp0-${userdomain}\n') + aclf.write('acl allow all bind exchange name=temp0-${userdomain}\n') + aclf.write('acl allow all unbind exchange name=temp0-${userdomain}\n') + aclf.write('acl allow all delete exchange name=temp0-${userdomain}\n') + aclf.write('acl allow all publish exchange name=temp0-${userdomain}\n') + + aclf.write('acl allow all publish exchange name=X routingkey=${userdomain}.cd.e\n') + aclf.write('acl allow all publish exchange name=X routingkey=a.*.${userdomain}\n') + aclf.write('acl allow all publish exchange name=X routingkey=b.#.${userdomain}\n') + aclf.write('acl allow all publish exchange name=X routingkey=*.${userdomain}.#.y\n') + + aclf.write('acl allow all create queue name=user-${user}\n') + aclf.write('acl allow all publish exchange name=U routingkey=${user}.cd.e\n') + aclf.write('acl allow all publish exchange name=U routingkey=a.*.${user}\n') + aclf.write('acl allow all publish exchange name=U routingkey=b.#.${user}\n') + aclf.write('acl allow all publish exchange name=U routingkey=*.${user}.#.y\n') + + aclf.write('acl allow all create queue name=domain-${domain}\n') + aclf.write('acl allow all publish exchange name=D routingkey=${domain}.cd.e\n') + aclf.write('acl allow all publish exchange name=D routingkey=a.*.${domain}\n') + aclf.write('acl allow all publish exchange name=D routingkey=b.#.${domain}\n') + aclf.write('acl allow all publish exchange name=D routingkey=*.${domain}.#.y\n') + + # Resolving ${user}_${domain} into ${userdomain} works for everything but routing keys + aclf.write('acl allow all create queue name=mixed-OK-${user}_${domain}\n') + # For routing keys ${user}_${domain} will be parsed into ${userdomain}. + # Routing keys not be found when the rule specifies ${user}_${domain}. + aclf.write('acl allow all publish exchange name=NOGO routingkey=${user}_${domain}.cd.e\n') + # This works since it is does not conflict with ${userdomain} + aclf.write('acl allow all publish exchange name=OK routingkey=${user}___${domain}.cd.e\n') + + aclf.write('acl deny-log all all\n') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + self.Lookup("alice@QPID", "create", "queue", "tmp-alice_QPID", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-tmp", {}, "allow") + self.Lookup("charlie@QPID", "create", "queue", "tmp-charlie_QPID-tmp", {}, "allow") + self.Lookup("dave@QPID", "create", "queue", "tmp-dave_QPID-tmp-dave_QPID", {}, "allow") + self.Lookup("ed@BIG.COM", "create", "queue", "tmp-ed_BIG_COM", {}, "allow") + self.Lookup("c.e.r@BIG.GER.COM", "create", "queue", "tmp-c_e_r_BIG_GER_COM", {}, "allow") + self.Lookup("c@", "create", "queue", "tmp-c_", {}, "allow") + self.Lookup("someuser", "create", "queue", "tmp-someuser", {}, "allow") + + self.Lookup("alice@QPID", "create", "queue", "tmp-${user}", {}, "deny-log") + + self.Lookup("bob@QPID", "create", "exchange", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "access", "exchange", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "bind", "exchange", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "unbind", "exchange", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "delete", "exchange", "temp0-bob_QPID", {}, "allow") + self.LookupPublish("bob@QPID", "temp0-bob_QPID", "x", "allow") + + self.Lookup("bob@QPID", "create", "queue", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "access", "queue", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "purge", "queue", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "consume", "queue", "temp0-bob_QPID", {}, "allow") + self.Lookup("bob@QPID", "delete", "queue", "temp0-bob_QPID", {}, "allow") + + self.Lookup("alice@QPID", "access", "queue", "temp0-bob_QPID", {}, "deny-log") + + # aclKey: "${userdomain}.cd.e" + self.LookupPublish("uPlain1@COMPANY", "X", "uPlain1_COMPANY.cd.e", "allow") + # aclKey: "a.*.${userdomain}" + self.LookupPublish("uStar1@COMPANY", "X", "a.xx.uStar1_COMPANY", "allow") + self.LookupPublish("uStar1@COMPANY", "X", "a.b", "deny-log") + # aclKey: "b.#.${userdomain}" + self.LookupPublish("uHash1@COMPANY", "X", "b.uHash1_COMPANY", "allow") + self.LookupPublish("uHash1@COMPANY", "X", "b.x.uHash1_COMPANY", "allow") + self.LookupPublish("uHash1@COMPANY", "X", "b..x.y.zz.uHash1_COMPANY", "allow") + self.LookupPublish("uHash1@COMPANY", "X", "b.uHash1_COMPANY.", "deny-log") + self.LookupPublish("uHash1@COMPANY", "X", "q.x.uHash1_COMPANY", "deny-log") + # aclKey: "*.${userdomain}.#.y" + self.LookupPublish("uMixed1@COMPANY", "X", "a.uMixed1_COMPANY.y", "allow") + self.LookupPublish("uMixed1@COMPANY", "X", "a.uMixed1_COMPANY.p.qq.y", "allow") + self.LookupPublish("uMixed1@COMPANY", "X", "a.a.uMixed1_COMPANY.y", "deny-log") + self.LookupPublish("uMixed1@COMPANY", "X", "aa.uMixed1_COMPANY.b.c", "deny-log") + self.LookupPublish("uMixed1@COMPANY.COM", "X", "a.uMixed1_COMPANY_COM.y", "allow") + + + self.Lookup("bob@QPID", "create", "queue", "user-bob", {}, "allow") + # aclKey: "${user}.cd.e" + self.LookupPublish("uPlain1@COMPANY", "U", "uPlain1.cd.e", "allow") + # aclKey: "a.*.${user}" + self.LookupPublish("uStar1@COMPANY", "U", "a.xx.uStar1", "allow") + self.LookupPublish("uStar1@COMPANY", "U", "a.b", "deny-log") + # aclKey: "b.#.${user}" + self.LookupPublish("uHash1@COMPANY", "U", "b.uHash1", "allow") + self.LookupPublish("uHash1@COMPANY", "U", "b.x.uHash1", "allow") + self.LookupPublish("uHash1@COMPANY", "U", "b..x.y.zz.uHash1", "allow") + self.LookupPublish("uHash1@COMPANY", "U", "b.uHash1.", "deny-log") + self.LookupPublish("uHash1@COMPANY", "U", "q.x.uHash1", "deny-log") + # aclKey: "*.${user}.#.y" + self.LookupPublish("uMixed1@COMPANY", "U", "a.uMixed1.y", "allow") + self.LookupPublish("uMixed1@COMPANY", "U", "a.uMixed1.p.qq.y", "allow") + self.LookupPublish("uMixed1@COMPANY", "U", "a.a.uMixed1.y", "deny-log") + self.LookupPublish("uMixed1@COMPANY", "U", "aa.uMixed1.b.c", "deny-log") + self.LookupPublish("uMixed1@COMPANY.COM", "U", "a.uMixed1.y", "allow") + + + self.Lookup("bob@QPID", "create", "queue", "domain-QPID", {}, "allow") + # aclKey: "${domain}.cd.e" + self.LookupPublish("uPlain1@COMPANY", "D", "COMPANY.cd.e", "allow") + # aclKey: "a.*.${domain}" + self.LookupPublish("uStar1@COMPANY", "D", "a.xx.COMPANY", "allow") + self.LookupPublish("uStar1@COMPANY", "D", "a.b", "deny-log") + # aclKey: "b.#.${domain}" + self.LookupPublish("uHash1@COMPANY", "D", "b.COMPANY", "allow") + self.LookupPublish("uHash1@COMPANY", "D", "b.x.COMPANY", "allow") + self.LookupPublish("uHash1@COMPANY", "D", "b..x.y.zz.COMPANY", "allow") + self.LookupPublish("uHash1@COMPANY", "D", "b.COMPANY.", "deny-log") + self.LookupPublish("uHash1@COMPANY", "D", "q.x.COMPANY", "deny-log") + # aclKey: "*.${domain}.#.y" + self.LookupPublish("uMixed1@COMPANY", "D", "a.COMPANY.y", "allow") + self.LookupPublish("uMixed1@COMPANY", "D", "a.COMPANY.p.qq.y", "allow") + self.LookupPublish("uMixed1@COMPANY", "D", "a.a.COMPANY.y", "deny-log") + self.LookupPublish("uMixed1@COMPANY", "D", "aa.COMPANY.b.c", "deny-log") + self.LookupPublish("uMixed1@COMPANY.COM", "D", "a.COMPANY_COM.y", "allow") + + self.Lookup("uPlain1@COMPANY", "create", "queue", "mixed-OK-uPlain1_COMPANY", {}, "allow") + self.LookupPublish("uPlain1@COMPANY", "NOGO", "uPlain1_COMPANY.cd.e", "deny-log") + self.LookupPublish("uPlain1@COMPANY", "OK", "uPlain1___COMPANY.cd.e", "allow") + + + #===================================== + # User name substitution details + #===================================== + # User name substitution allows for three flavors of keyword in the Acl file. + # Given a user name of bob.user@QPID.COM the keywords are normalized and resolve as follows: + # ${userdomain} - bob_user_QPID_COM + # ${user} - bob_user + # ${domain} - QPID_COM + # + # The following substitution tests are very similar but differ in the flavor of keyword used + # in the rules. The tests results using the different keywords differ slightly in how permissive + # the rules become. + # ${userdomain} limits access to one authenticated user + # ${user} limits access to a user name regardless of user's domain + # ${domain} limits access to a domain regardless of user name + # + + def test_user_name_substitution_userdomain(self): + """ + Test a setup where users can create, bind, and publish to a main exchange and queue. + Allow access to a single alternate exchange and queue. + """ + aclf = self.get_acl_file() + 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') + # Create primary queue and exchange: + # allow predefined alternate + # deny any other alternate + # allow no alternate + aclf.write('acl allow all create queue name=${userdomain}-work alternate=${userdomain}-work2\n') + aclf.write('acl deny all create queue name=${userdomain}-work alternate=*\n') + aclf.write('acl allow all create queue name=${userdomain}-work\n') + aclf.write('acl allow all create exchange name=${userdomain}-work alternate=${userdomain}-work2\n') + aclf.write('acl deny all create exchange name=${userdomain}-work alternate=*\n') + aclf.write('acl allow all create exchange name=${userdomain}-work\n') + # Create backup queue and exchange + # Deny any alternate + aclf.write('acl deny all create queue name=${userdomain}-work2 alternate=*\n') + aclf.write('acl allow all create queue name=${userdomain}-work2\n') + aclf.write('acl deny all create exchange name=${userdomain}-work2 alternate=*\n') + aclf.write('acl allow all create exchange name=${userdomain}-work2\n') + # Bind/unbind primary exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all bind exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n') + aclf.write('acl allow all unbind exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n') + # Bind/unbind backup exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all bind exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n') + aclf.write('acl allow all unbind exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n') + # Access primary exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all access exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n') + # Access backup exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all access exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n') + # Publish primary exchange + # Use only predefined routingkey + aclf.write('acl allow all publish exchange name=${userdomain}-work routingkey=${userdomain}\n') + # Publish backup exchange + # Use only predefined routingkey + aclf.write('acl allow all publish exchange name=${userdomain}-work2 routingkey=${userdomain}\n') + # deny mode + aclf.write('acl deny all all\n') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + # create queues + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work2", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "joe_QPID-work", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "joe_QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work3", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work", {"alternate":"bob_QPID-work2"}, "allow") + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work", {"alternate":"joe_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work2", {"alternate":"someexchange"}, "deny") + # create exchanges + self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work", {}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work2",{}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "joe_QPID-work", {}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "joe_QPID-work2",{}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work3",{}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work", {"alternate":"bob_QPID-work2"}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work2",{"alternate":"someexchange"}, "deny") + # bind/unbind/access + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", { "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow") + self.Lookup("bob@QPID", "bind", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny") + + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", { "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow") + self.Lookup("bob@QPID", "bind", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny") + + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", { "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow") + self.Lookup("bob@QPID", "unbind", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny") + + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", { "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow") + self.Lookup("bob@QPID", "unbind", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny") + + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", { "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow") + self.Lookup("bob@QPID", "access", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny") + + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", { "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow") + self.Lookup("bob@QPID", "access", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny") + # publish + self.LookupPublish("bob@QPID", "bob_QPID-work", "bob_QPID", "allow") + self.LookupPublish("bob@QPID", "bob_QPID-work2", "bob_QPID", "allow") + self.LookupPublish("bob@QPID", "joe_QPID-work", "bob_QPID", "deny") + self.LookupPublish("bob@QPID", "joe_QPID-work2", "bob_QPID", "deny") + self.LookupPublish("bob@QPID", "bob_QPID-work", "joe_QPID", "deny") + self.LookupPublish("bob@QPID", "bob_QPID-work2", "joe_QPID", "deny") + + + def test_user_name_substitution_user(self): + """ + Test a setup where users can create, bind, and publish to a main exchange and queue. + Allow access to a single backup exchange and queue. + """ + aclf = self.get_acl_file() + 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') + # Create primary queue and exchange + # allow predefined alternate + # deny any other alternate + # allow no alternate + aclf.write('acl allow all create queue name=${user}-work alternate=${user}-work2\n') + aclf.write('acl deny all create queue name=${user}-work alternate=*\n') + aclf.write('acl allow all create queue name=${user}-work\n') + aclf.write('acl allow all create exchange name=${user}-work alternate=${user}-work2\n') + aclf.write('acl deny all create exchange name=${user}-work alternate=*\n') + aclf.write('acl allow all create exchange name=${user}-work\n') + # Create backup queue and exchange + # Deny any alternate + aclf.write('acl deny all create queue name=${user}-work2 alternate=*\n') + aclf.write('acl allow all create queue name=${user}-work2\n') + aclf.write('acl deny all create exchange name=${user}-work2 alternate=*\n') + aclf.write('acl allow all create exchange name=${user}-work2\n') + # Bind/unbind primary exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all bind exchange name=${user}-work routingkey=${user} queuename=${user}-work\n') + aclf.write('acl allow all unbind exchange name=${user}-work routingkey=${user} queuename=${user}-work\n') + # Bind/unbind backup exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all bind exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n') + aclf.write('acl allow all unbind exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n') + # Access primary exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all access exchange name=${user}-work routingkey=${user} queuename=${user}-work\n') + # Access backup exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all access exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n') + # Publish primary exchange + # Use only predefined routingkey + aclf.write('acl allow all publish exchange name=${user}-work routingkey=${user}\n') + # Publish backup exchange + # Use only predefined routingkey + aclf.write('acl allow all publish exchange name=${user}-work2 routingkey=${user}\n') + # deny mode + aclf.write('acl deny all all\n') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + # create queues + self.Lookup("bob@QPID", "create", "queue", "bob-work", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "bob-work2", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "joe-work", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "joe-work2", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "bob-work3", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "bob-work", {"alternate":"bob-work2"}, "allow") + self.Lookup("bob@QPID", "create", "queue", "bob-work", {"alternate":"joe-work2"}, "deny") + self.Lookup("bob@QPID", "create", "queue", "bob-work2", {"alternate":"someexchange"},"deny") + # create exchanges + self.Lookup("bob@QPID", "create", "exchange", "bob-work", {}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "bob-work2",{}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "joe-work", {}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "joe-work2",{}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "bob-work3",{}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "bob-work", {"alternate":"bob-work2"}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "bob-work2",{"alternate":"someexchange"},"deny") + # bind/unbind/access + self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work", { "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow") + self.Lookup("bob@QPID", "bind", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny") + + self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", { "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow") + self.Lookup("bob@QPID", "bind", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny") + + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", { "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow") + self.Lookup("bob@QPID", "unbind", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny") + + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", { "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow") + self.Lookup("bob@QPID", "unbind", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny") + + self.Lookup("bob@QPID", "access", "exchange", "bob-work", {}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work", { "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow") + self.Lookup("bob@QPID", "access", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny") + + self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work2", { "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow") + self.Lookup("bob@QPID", "access", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny") + # publish + self.LookupPublish("bob@QPID", "bob-work", "bob", "allow") + self.LookupPublish("bob@QPID", "bob-work2", "bob", "allow") + self.LookupPublish("bob@QPID", "joe-work", "bob", "deny") + self.LookupPublish("bob@QPID", "joe-work2", "bob", "deny") + self.LookupPublish("bob@QPID", "bob-work", "joe", "deny") + self.LookupPublish("bob@QPID", "bob-work2", "joe", "deny") + + + def test_user_name_substitution_domain(self): + """ + Test a setup where users can create, bind, and publish to a main exchange and queue. + Allow access to a single backup exchange and queue. + """ + aclf = self.get_acl_file() + 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') + # Create primary queue and exchange + # allow predefined alternate + # deny any other alternate + # allow no alternate + aclf.write('acl allow all create queue name=${domain}-work alternate=${domain}-work2\n') + aclf.write('acl deny all create queue name=${domain}-work alternate=*\n') + aclf.write('acl allow all create queue name=${domain}-work\n') + aclf.write('acl allow all create exchange name=${domain}-work alternate=${domain}-work2\n') + aclf.write('acl deny all create exchange name=${domain}-work alternate=*\n') + aclf.write('acl allow all create exchange name=${domain}-work\n') + # Create backup queue and exchange + # Deny any alternate + aclf.write('acl deny all create queue name=${domain}-work2 alternate=*\n') + aclf.write('acl allow all create queue name=${domain}-work2\n') + aclf.write('acl deny all create exchange name=${domain}-work2 alternate=*\n') + aclf.write('acl allow all create exchange name=${domain}-work2\n') + # Bind/unbind primary exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all bind exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n') + aclf.write('acl allow all unbind exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n') + # Bind/unbind backup exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all bind exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n') + aclf.write('acl allow all unbind exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n') + # Access primary exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all access exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n') + # Access backup exchange + # Use only predefined routingkey and queuename + aclf.write('acl allow all access exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n') + # Publish primary exchange + # Use only predefined routingkey + aclf.write('acl allow all publish exchange name=${domain}-work routingkey=${domain}\n') + # Publish backup exchange + # Use only predefined routingkey + aclf.write('acl allow all publish exchange name=${domain}-work2 routingkey=${domain}\n') + # deny mode + aclf.write('acl deny all all\n') + aclf.close() + + result = self.reload_acl() + if (result): + self.fail(result) + + # create queues + self.Lookup("bob@QPID", "create", "queue", "QPID-work", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "QPID-work2", {}, "allow") + self.Lookup("bob@QPID", "create", "queue", "QPID-work3", {}, "deny") + self.Lookup("bob@QPID", "create", "queue", "QPID-work", {"alternate":"QPID-work2"}, "allow") + self.Lookup("bob@QPID", "create", "queue", "QPID-work", {"alternate":"bob_QPID-work2"},"deny") + self.Lookup("bob@QPID", "create", "queue", "QPID-work", {"alternate":"joe_QPID-work2"},"deny") + self.Lookup("bob@QPID", "create", "queue", "QPID-work2", {"alternate":"someexchange"}, "deny") + # create exchanges + self.Lookup("bob@QPID", "create", "exchange", "QPID-work", {}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "QPID-work2",{}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "QPID-work3",{}, "deny") + self.Lookup("bob@QPID", "create", "exchange", "QPID-work", {"alternate":"QPID-work2"}, "allow") + self.Lookup("bob@QPID", "create", "exchange", "QPID-work2",{"alternate":"someexchange"}, "deny") + # bind/unbind/access + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {"routingkey":"QPID"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", { "queuename":"QPID-work"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow") + + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {"routingkey":"QPID"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", { "queuename":"QPID-work2"}, "deny") + self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow") + + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {"routingkey":"QPID"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", { "queuename":"QPID-work"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow") + + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {"routingkey":"QPID"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", { "queuename":"QPID-work2"}, "deny") + self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow") + + self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {"routingkey":"QPID"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "QPID-work", { "queuename":"QPID-work"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow") + + self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {"routingkey":"QPID"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", { "queuename":"QPID-work2"}, "deny") + self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow") + # publish + self.LookupPublish("bob@QPID", "QPID-work", "QPID", "allow") + self.LookupPublish("bob@QPID", "QPID-work2", "QPID", "allow") + self.LookupPublish("joe@QPID", "QPID-work", "QPID", "allow") + self.LookupPublish("joe@QPID", "QPID-work2", "QPID", "allow") + + class BrokerAdmin: def __init__(self, broker, username=None, password=None): self.connection = qpid.messaging.Connection(broker) diff --git a/cpp/src/tests/cluster.cmake b/cpp/src/tests/cluster.cmake deleted file mode 100644 index 31e2d337d1..0000000000 --- a/cpp/src/tests/cluster.cmake +++ /dev/null @@ -1,90 +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. -# - -# -# Cluster tests cmake fragment, to be included in CMakeLists.txt -# - -add_executable (failover_soak failover_soak.cpp ForkedBroker.cpp ${platform_test_additions}) -target_link_libraries (failover_soak qpidclient) -remember_location(failover_soak) - -add_executable (cluster_authentication_soak cluster_authentication_soak.cpp ForkedBroker.cpp ${platform_test_additions}) -target_link_libraries (cluster_authentication_soak qpidclient) -remember_location(cluster_authentication_soak) - -set (cluster_test_SOURCES - cluster_test - unit_test - ClusterFixture - ForkedBroker - PartialFailure - ClusterFailover - InitialStatusMap - StoreStatus - ) -add_executable (cluster_test ${cluster_test_SOURCES} ${platform_test_additions}) -target_link_libraries (cluster_test ${qpid_test_boost_libs} qpidclient qpidbroker cluster_shared) -remember_location(cluster_test) - -add_test (cluster_test ${CMAKE_CURRENT_SOURCE_DIR}/run_cluster_test${test_script_suffix}) -add_test (cluster_tests ${CMAKE_CURRENT_SOURCE_DIR}/run_cluster_tests${test_script_suffix}) -add_test (cluster_read_credit ${CMAKE_CURRENT_SOURCE_DIR}/cluster_read_credit${test_script_suffix}) -add_test (cluster_test_watchdog ${CMAKE_CURRENT_SOURCE_DIR}/test_watchdog${test_script_suffix}) -add_test (federated_cluster_test ${CMAKE_CURRENT_SOURCE_DIR}/federated_cluster_test${test_script_suffix}) -add_test (clustered_replication_test ${CMAKE_CURRENT_SOURCE_DIR}/clustered_replication_test${test_script_suffix}) - -# FIXME aconway 2009-12-01: translate to cmake -# # Clean up after cluster_test and start_cluster -# CLEANFILES += cluster_test.acl cluster.ports - -# EXTRA_DIST += \ -# cpg_check.sh.in \ -# run_cluster_test \ -# cluster_read_credit \ -# test_watchdog \ -# start_cluster \ -# stop_cluster \ -# restart_cluster \ -# cluster_python_tests \ -# cluster_python_tests_failing.txt \ -# federated_cluster_test \ -# clustered_replication_test \ -# run_cluster_tests \ -# run_long_cluster_tests \ -# testlib.py \ -# cluster_tests.py \ -# long_cluster_tests.py \ -# cluster_tests.fail - -# LONG_TESTS += \ -# run_long_cluster_tests \ -# start_cluster \ -# cluster_python_tests \ -# stop_cluster - -# qpidtest_PROGRAMS += cluster_test - -# cluster_test_SOURCES = \ - -# cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework - -# qpidtest_SCRIPTS += run_cluster_tests cluster_tests.py run_long_cluster_tests long_cluster_tests.py testlib.py cluster_tests.fail - -# endif diff --git a/cpp/src/tests/cluster.mk b/cpp/src/tests/cluster.mk deleted file mode 100644 index 852b2dda8c..0000000000 --- a/cpp/src/tests/cluster.mk +++ /dev/null @@ -1,102 +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 cluster scripts and extra files in distribution even if -# we're not configured for cluster. - -# Useful scripts for doing cluster testing. -CLUSTER_TEST_SCRIPTS_LIST= \ - allhosts rsynchosts \ - qpid-build-rinstall qpid-src-rinstall \ - qpid-test-cluster - -EXTRA_DIST += \ - $(CLUSTER_TEST_SCRIPTS_LIST) \ - cpg_check.sh.in \ - run_cluster_test \ - cluster_read_credit \ - test_watchdog \ - start_cluster \ - stop_cluster \ - restart_cluster \ - cluster_python_tests \ - cluster_python_tests_failing.txt \ - federated_cluster_test \ - clustered_replication_test \ - run_cluster_tests \ - run_long_cluster_tests \ - testlib.py \ - brokertest.py \ - cluster_tests.py \ - cluster_test_logs.py \ - long_cluster_tests.py \ - cluster_tests.fail - - -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. -# - -TESTS += \ - run_cluster_test \ - cluster_read_credit \ - test_watchdog \ - run_cluster_tests \ - federated_cluster_test \ - clustered_replication_test - -# Clean up after cluster_test and start_cluster -CLEANFILES += cluster_test.acl cluster.ports - -LONG_TESTS += \ - run_long_cluster_tests \ - start_cluster \ - cluster_python_tests \ - stop_cluster - -qpidexectest_PROGRAMS += cluster_test - -cluster_test_SOURCES = \ - cluster_test.cpp \ - unit_test.cpp \ - ClusterFixture.cpp \ - ClusterFixture.h \ - ForkedBroker.h \ - ForkedBroker.cpp \ - PartialFailure.cpp \ - ClusterFailover.cpp \ - InitialStatusMap.cpp - -# Moved this file here from cluster_test_SOURCES as it breaks the autotools build, but not the cmake -# build and so we need to make sure it is present in the tarball -EXTRA_DIST += StoreStatus.cpp - -cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework - -qpidexectest_SCRIPTS += run_cluster_tests brokertest.py cluster_tests.py cluster_test_logs.py run_long_cluster_tests long_cluster_tests.py testlib.py cluster_tests.fail -qpidexectest_SCRIPTS += $(CLUSTER_TEST_SCRIPTS_LIST) - -endif diff --git a/cpp/src/tests/ha_tests.py b/cpp/src/tests/ha_tests.py index d25281eed5..310ef844bd 100755 --- a/cpp/src/tests/ha_tests.py +++ b/cpp/src/tests/ha_tests.py @@ -111,9 +111,11 @@ class HaBroker(Broker): def wait_status(self, status): def try_get_status(): # Ignore ConnectionError, the broker may not be up yet. - try: return self.ha_status() == status; + try: + self._status = self.ha_status() + return self._status == status; except ConnectionError: return False - assert retry(try_get_status, timeout=20), "%s status != %r"%(self, status) + assert retry(try_get_status, timeout=20), "%s %r != %r"%(self, self._status, status) # FIXME aconway 2012-05-01: do direct python call to qpid-config code. def qpid_config(self, args): @@ -581,15 +583,7 @@ class ReplicationTests(BrokerTest): 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 - # - # expect = sorted(priorities,reverse=True)[0:5] - expect = [9,9,9,9,2] + expect = sorted(priorities,reverse=True)[0:5] primary.assert_browse("q", expect, transform=lambda m: m.priority) backup.assert_browse_backup("q", expect, transform=lambda m: m.priority) @@ -963,7 +957,7 @@ class RecoveryTests(BrokerTest): """ cluster = HaCluster(self, 3, args=["--ha-backup-timeout=0.5"]); cluster[0].wait_status("active") # Primary ready - for b in cluster[1:4]: b.wait_status("ready") # Backups ready + for b in cluster[1:3]: b.wait_status("ready") # Backups ready for i in [0,1]: cluster.kill(i, False) cluster[2].promote() # New primary, backups will be 1 and 2 cluster[2].wait_status("recovering") diff --git a/cpp/src/tests/sasl.mk b/cpp/src/tests/sasl.mk index 11731dcf40..8c31192635 100644 --- a/cpp/src/tests/sasl.mk +++ b/cpp/src/tests/sasl.mk @@ -20,13 +20,6 @@ # 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 @@ -39,30 +32,12 @@ TESTS += sasl_fed 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 \ +EXTRA_DIST += sasl_fed \ sasl_fed_ex \ - run_cluster_authentication_soak \ sasl_fed_ex_dynamic \ sasl_fed_ex_link \ sasl_fed_ex_queue \ sasl_fed_ex_route \ - sasl_fed_ex_dynamic_cluster \ - sasl_fed_ex_link_cluster \ - sasl_fed_ex_queue_cluster \ - sasl_fed_ex_route_cluster \ sasl_no_dir diff --git a/cpp/src/tests/storePerftools/asyncPerf/MessageProducer.cpp b/cpp/src/tests/storePerftools/asyncPerf/MessageProducer.cpp index b9a5e2a211..f5c47c796d 100644 --- a/cpp/src/tests/storePerftools/asyncPerf/MessageProducer.cpp +++ b/cpp/src/tests/storePerftools/asyncPerf/MessageProducer.cpp @@ -25,6 +25,7 @@ #include "TestOptions.h" +#include "qpid/asyncStore/PersistableMessageContext.h" #include "qpid/broker/SimpleMessage.h" #include "qpid/broker/SimpleQueue.h" #include "qpid/broker/SimpleTxnBuffer.h" @@ -65,7 +66,10 @@ MessageProducer::runProducers() { tb = new qpid::broker::SimpleTxnBuffer(m_resultQueue); } for (uint32_t numMsgs=0; numMsgs<m_perfTestParams.m_numMsgs && !m_stopFlag; ++numMsgs) { - boost::intrusive_ptr<qpid::broker::SimpleMessage> msg(new qpid::broker::SimpleMessage(m_msgData, m_perfTestParams.m_msgSize, m_store)); + boost::intrusive_ptr<qpid::asyncStore::PersistableMessageContext> msgCtxt(new qpid::asyncStore::PersistableMessageContext(m_store)); + boost::intrusive_ptr<qpid::broker::AsyncCompletion> ingressCompl(new qpid::broker::AsyncCompletion); + msgCtxt->setIngressCompletion(ingressCompl); + boost::intrusive_ptr<qpid::broker::SimpleMessage> msg(new qpid::broker::SimpleMessage(m_msgData, m_perfTestParams.m_msgSize, msgCtxt)); if (useTxns) { boost::shared_ptr<qpid::broker::SimpleTxnPublish> op(new qpid::broker::SimpleTxnPublish(msg)); op->deliverTo(m_queue); diff --git a/cpp/src/tests/test_store.cpp b/cpp/src/tests/test_store.cpp index 257e77b6b4..83f6a5e4b1 100644 --- a/cpp/src/tests/test_store.cpp +++ b/cpp/src/tests/test_store.cpp @@ -34,6 +34,7 @@ #include "qpid/broker/NullMessageStore.h" #include "qpid/broker/Broker.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/AMQFrame.h" #include "qpid/log/Statement.h" #include "qpid/Plugin.h" @@ -95,7 +96,7 @@ class TestStore : public NullMessageStore { const boost::intrusive_ptr<PersistableMessage>& pmsg, const PersistableQueue& ) { - Message* msg = dynamic_cast<Message*>(pmsg.get()); + qpid::broker::amqp_0_10::MessageTransfer* msg = dynamic_cast<qpid::broker::amqp_0_10::MessageTransfer*>(pmsg.get()); assert(msg); // Dump the message if there is a dump file. |