diff options
author | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
---|---|---|
committer | Kim van der Riet <kpvdr@apache.org> | 2012-05-04 15:39:19 +0000 |
commit | 633c33f224f3196f3f9bd80bd2e418d8143fea06 (patch) | |
tree | 1391da89470593209466df68c0b40b89c14963b1 /cpp/src/qpid | |
parent | c73f9286ebff93a6c8dbc29cf05e258c4b55c976 (diff) | |
download | qpid-python-633c33f224f3196f3f9bd80bd2e418d8143fea06.tar.gz |
QPID-3858: Updated branch - merged from trunk r.1333987
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1334037 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp/src/qpid')
152 files changed, 3842 insertions, 2457 deletions
diff --git a/cpp/src/qpid/Options.cpp b/cpp/src/qpid/Options.cpp index 4b13e349f5..35787aa8f3 100644 --- a/cpp/src/qpid/Options.cpp +++ b/cpp/src/qpid/Options.cpp @@ -186,13 +186,14 @@ void Options::parse(int argc, char const* const* argv, const std::string& config } } -CommonOptions::CommonOptions(const string& name, const string& configfile) - : Options(name), config(configfile) +CommonOptions::CommonOptions(const string& name, const string& configfile, const string& clientfile) + : Options(name), config(configfile), clientConfig(clientfile) { addOptions() ("help,h", optValue(help), "Displays the help message") ("version,v", optValue(version), "Displays version information") - ("config", optValue(config, "FILE"), "Reads configuration from FILE"); + ("config", optValue(config, "FILE"), "Reads configuration from FILE") + ("client-config", optValue(clientConfig, "FILE"), "Reads client configuration from FILE (for cluster interconnect)"); } diff --git a/cpp/src/qpid/Url.cpp b/cpp/src/qpid/Url.cpp index 2061499ec3..840f46e928 100644 --- a/cpp/src/qpid/Url.cpp +++ b/cpp/src/qpid/Url.cpp @@ -111,7 +111,8 @@ ssl_addr = "ssl:" host [":" port] */ class UrlParser { public: - UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {} + UrlParser(Url& u, const char* s, const std::string& defaultProtocol_=Address::TCP) : url(u), text(s), end(s+strlen(s)), i(s), + defaultProtocol(defaultProtocol_) {} bool parse() { literal("amqp:"); // Optional userPass(); // Optional @@ -135,7 +136,7 @@ class UrlParser { bool comma() { return literal(","); } bool protocolAddr() { - Address addr(Address::TCP, "", Address::AMQP_PORT); // Set up defaults + Address addr(defaultProtocol, "", Address::AMQP_PORT); // Set up defaults protocolTag(addr.protocol); // Optional bool ok = (host(addr.host) && (literal(":") ? port(addr.port) : true)); @@ -244,20 +245,28 @@ class UrlParser { const char* text; const char* end; const char* i; + const std::string defaultProtocol; }; const string UrlParser::LOCALHOST("127.0.0.1"); void Url::parse(const char* url) { - parseNoThrow(url); + parse(url, Address::TCP); +} +void Url::parse(const char* url, const std::string& defaultProtocol) { + parseNoThrow(url, defaultProtocol); if (empty()) throw Url::Invalid(QPID_MSG("Invalid URL: " << url)); } void Url::parseNoThrow(const char* url) { + parseNoThrow(url, Address::TCP); +} + +void Url::parseNoThrow(const char* url, const std::string& defaultProtocol) { clear(); cache.clear(); - if (!UrlParser(*this, url).parse()) + if (!UrlParser(*this, url, defaultProtocol).parse()) clear(); } diff --git a/cpp/src/qpid/UrlArray.cpp b/cpp/src/qpid/UrlArray.cpp index 489309c8ad..9ebacbd945 100644 --- a/cpp/src/qpid/UrlArray.cpp +++ b/cpp/src/qpid/UrlArray.cpp @@ -20,6 +20,8 @@ */ #include "UrlArray.h" +#include <qpid/framing/FieldValue.h> + namespace qpid { std::vector<Url> urlArrayToVector(const framing::Array& array) { diff --git a/cpp/src/qpid/UrlArray.h b/cpp/src/qpid/UrlArray.h index 8b11df5c73..ce9e42f248 100644 --- a/cpp/src/qpid/UrlArray.h +++ b/cpp/src/qpid/UrlArray.h @@ -23,7 +23,6 @@ */ #include "qpid/framing/Array.h" -#include "qpid/framing/FieldValue.h" #include "qpid/Url.h" #include <vector> diff --git a/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp index 12bf13018c..917c2e3398 100644 --- a/cpp/src/qpid/acl/Acl.cpp +++ b/cpp/src/qpid/acl/Acl.cpp @@ -17,6 +17,7 @@ */ #include "qpid/acl/Acl.h" +#include "qpid/acl/AclConnectionCounter.h" #include "qpid/acl/AclData.h" #include "qpid/acl/AclValidator.h" #include "qpid/sys/Mutex.h" @@ -26,8 +27,11 @@ #include "qpid/Options.h" #include "qpid/log/Logger.h" #include "qpid/types/Variant.h" +#include "qmf/org/apache/qpid/acl/ArgsAclLookup.h" +#include "qmf/org/apache/qpid/acl/ArgsAclLookupPublish.h" #include "qmf/org/apache/qpid/acl/Package.h" #include "qmf/org/apache/qpid/acl/EventAllow.h" +#include "qmf/org/apache/qpid/acl/EventConnectionDeny.h" #include "qmf/org/apache/qpid/acl/EventDeny.h" #include "qmf/org/apache/qpid/acl/EventFileLoaded.h" #include "qmf/org/apache/qpid/acl/EventFileLoadFailed.h" @@ -35,7 +39,6 @@ #include <map> #include <boost/shared_ptr.hpp> -#include <boost/utility/in_place_factory.hpp> using namespace std; using namespace qpid::acl; @@ -47,7 +50,8 @@ using qpid::management::Manageable; using qpid::management::Args; namespace _qmf = qmf::org::apache::qpid::acl; -Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0) +Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0), + connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp)) { agent = broker->getManagementAgent(); @@ -62,14 +66,30 @@ Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(fals throw Exception("Could not read ACL file " + errorString); if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0); } + broker->getConnectionObservers().add(connectionCounter); QPID_LOG(info, "ACL Plugin loaded"); if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1); } -bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params) + +void Acl::reportConnectLimit(const std::string user, const std::string addr) +{ + if (mgmtObject!=0) + mgmtObject->inc_connectionDenyCount(); + + agent->raiseEvent(_qmf::EventConnectionDeny(user, addr)); +} + + +bool Acl::authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name, + std::map<Property, std::string>* params) { boost::shared_ptr<AclData> dataLocal; - { + { Mutex::ScopedLock locker(dataLock); dataLocal = data; //rcu copy } @@ -81,7 +101,12 @@ bool Acl::authorise(const std::string& id, const Action& action, const ObjectTyp return result(aclreslt, id, action, objType, name); } -bool Acl::authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey) +bool Acl::authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey) { boost::shared_ptr<AclData> dataLocal; { @@ -96,31 +121,50 @@ bool Acl::authorise(const std::string& id, const Action& action, const ObjectTyp } -bool Acl::result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name) +bool Acl::result( + const AclResult& aclreslt, + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name) { + bool result(false); + switch (aclreslt) { case ALLOWLOG: - QPID_LOG(info, "ACL Allow id:" << id <<" action:" << AclHelper::getActionStr(action) << - " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name ); + QPID_LOG(info, "ACL Allow id:" << id + << " action:" << AclHelper::getActionStr(action) + << " ObjectType:" << AclHelper::getObjectTypeStr(objType) + << " Name:" << name ); agent->raiseEvent(_qmf::EventAllow(id, AclHelper::getActionStr(action), - AclHelper::getObjectTypeStr(objType), - name, types::Variant::Map())); + AclHelper::getObjectTypeStr(objType), + name, types::Variant::Map())); + // FALLTHROUGH case ALLOW: - return true; - case DENY: - if (mgmtObject!=0) mgmtObject->inc_aclDenyCount(); - return false; + result = true; + break; + case DENYLOG: - if (mgmtObject!=0) mgmtObject->inc_aclDenyCount(); - default: - QPID_LOG(info, "ACL Deny id:" << id << " action:" << AclHelper::getActionStr(action) << " ObjectType:" << AclHelper::getObjectTypeStr(objType) << " Name:" << name); + QPID_LOG(info, "ACL Deny id:" << id + << " action:" << AclHelper::getActionStr(action) + << " ObjectType:" << AclHelper::getObjectTypeStr(objType) + << " Name:" << name); agent->raiseEvent(_qmf::EventDeny(id, AclHelper::getActionStr(action), - AclHelper::getObjectTypeStr(objType), - name, types::Variant::Map())); - return false; + AclHelper::getObjectTypeStr(objType), + name, types::Variant::Map())); + // FALLTHROUGH + case DENY: + if (mgmtObject!=0) + mgmtObject->inc_aclDenyCount(); + result = false; + break; + + default: + assert (false); } - return false; + + return result; } bool Acl::readAclFile(std::string& errorText) @@ -129,7 +173,7 @@ bool Acl::readAclFile(std::string& errorText) return readAclFile(aclValues.aclFile, errorText); } -bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { +bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { boost::shared_ptr<AclData> d(new AclData); AclReader ar; if (ar.read(aclFile, d)){ @@ -142,17 +186,17 @@ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { AclValidator validator; validator.validate(d); - { + { Mutex::ScopedLock locker(dataLock); data = d; } transferAcl = data->transferAcl; // any transfer ACL if (data->transferAcl){ - QPID_LOG(debug,"Transfer ACL is Enabled!"); + QPID_LOG(debug,"ACL: Transfer ACL is Enabled!"); } - data->aclSource = aclFile; + data->aclSource = aclFile; if (mgmtObject!=0){ mgmtObject->set_transferAcl(transferAcl?1:0); mgmtObject->set_policyFile(aclFile); @@ -164,17 +208,92 @@ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) { return true; } -Acl::~Acl(){} + +// +// management lookup function performs general query on acl engine +// +Manageable::status_t Acl::lookup(qpid::management::Args& args, std::string& text) +{ + _qmf::ArgsAclLookup& ioArgs = (_qmf::ArgsAclLookup&) args; + Manageable::status_t result(STATUS_USER); + + try { + ObjectType objType = AclHelper::getObjectType(ioArgs.i_object); + Action action = AclHelper::getAction( ioArgs.i_action); + std::map<Property, std::string> propertyMap; + for (::qpid::types::Variant::Map::const_iterator + iMapIter = ioArgs.i_propertyMap.begin(); + iMapIter != ioArgs.i_propertyMap.end(); + iMapIter++) + { + Property property = AclHelper::getProperty(iMapIter->first); + propertyMap.insert(make_pair(property, iMapIter->second)); + } + + boost::shared_ptr<AclData> dataLocal; + { + Mutex::ScopedLock locker(dataLock); + dataLocal = data; //rcu copy + } + AclResult aclResult = dataLocal->lookup( + ioArgs.i_userId, + action, + objType, + ioArgs.i_objectName, + &propertyMap); + + ioArgs.o_result = AclHelper::getAclResultStr(aclResult); + result = STATUS_OK; + + } catch (const std::exception& e) { + std::ostringstream oss; + oss << "AclLookup invalid name : " << e.what(); + ioArgs.o_result = oss.str(); + text = oss.str(); + } + + return result; +} + + +// +// management lookupPublish function performs fastpath +// PUBLISH EXCHANGE query on acl engine +// +Manageable::status_t Acl::lookupPublish(qpid::management::Args& args, std::string& /*text*/) +{ + _qmf::ArgsAclLookupPublish& ioArgs = (_qmf::ArgsAclLookupPublish&) args; + boost::shared_ptr<AclData> dataLocal; + { + Mutex::ScopedLock locker(dataLock); + dataLocal = data; //rcu copy + } + AclResult aclResult = dataLocal->lookup( + ioArgs.i_userId, + ACT_PUBLISH, + OBJ_EXCHANGE, + ioArgs.i_exchangeName, + ioArgs.i_routingKey); + + ioArgs.o_result = AclHelper::getAclResultStr(aclResult); + + return STATUS_OK; +} + + +Acl::~Acl(){ + broker->getConnectionObservers().remove(connectionCounter); +} ManagementObject* Acl::GetManagementObject(void) const { return (ManagementObject*) mgmtObject; } -Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, string& text) +Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& args, string& text) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; - QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]"); + QPID_LOG (debug, "ACL: Queue::ManagementMethod [id=" << methodId << "]"); switch (methodId) { @@ -185,6 +304,14 @@ Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, s else status = Manageable::STATUS_USER; break; + + case _qmf::Acl::METHOD_LOOKUP : + status = lookup(args, text); + break; + + case _qmf::Acl::METHOD_LOOKUPPUBLISH : + status = lookupPublish(args, text); + break; } return status; diff --git a/cpp/src/qpid/acl/Acl.h b/cpp/src/qpid/acl/Acl.h index 77f43838de..c3451018ef 100644 --- a/cpp/src/qpid/acl/Acl.h +++ b/cpp/src/qpid/acl/Acl.h @@ -30,6 +30,7 @@ #include "qmf/org/apache/qpid/acl/Acl.h" #include "qpid/sys/Mutex.h" +#include <boost/shared_ptr.hpp> #include <map> #include <string> @@ -40,9 +41,12 @@ class Broker; } namespace acl { +class ConnectionCounter; struct AclValues { - std::string aclFile; + std::string aclFile; + uint32_t aclMaxConnectPerUser; + uint32_t aclMaxConnectPerIp; }; @@ -50,37 +54,56 @@ class Acl : public broker::AclModule, public RefCounted, public management::Mana { private: - acl::AclValues aclValues; - broker::Broker* broker; - bool transferAcl; - boost::shared_ptr<AclData> data; - qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle - qpid::management::ManagementAgent* agent; - mutable qpid::sys::Mutex dataLock; + acl::AclValues aclValues; + broker::Broker* broker; + bool transferAcl; + boost::shared_ptr<AclData> data; + qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle + qpid::management::ManagementAgent* agent; + mutable qpid::sys::Mutex dataLock; + boost::shared_ptr<ConnectionCounter> connectionCounter; public: - Acl (AclValues& av, broker::Broker& b); - - void initialize(); - - inline virtual bool doTransferAcl() {return transferAcl;}; - - // create specilied authorise methods for cases that need faster matching as needed. - virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0); - virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName,const std::string& RoutingKey); - - virtual ~Acl(); + Acl (AclValues& av, broker::Broker& b); + + void reportConnectLimit(const std::string user, const std::string addr); + + inline virtual bool doTransferAcl() { + return transferAcl; + }; + +// create specilied authorise methods for cases that need faster matching as needed. + virtual bool authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name, + std::map<Property, std::string>* params=0); + + virtual bool authorise( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey); + + virtual ~Acl(); private: - bool result(const AclResult& aclreslt, const std::string& id, const Action& action, const ObjectType& objType, const std::string& name); - bool readAclFile(std::string& errorText); - bool readAclFile(std::string& aclFile, std::string& errorText); - virtual qpid::management::ManagementObject* GetManagementObject(void) const; - virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + bool result( + const AclResult& aclreslt, + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name); + bool readAclFile(std::string& errorText); + bool readAclFile(std::string& aclFile, std::string& errorText); + Manageable::status_t lookup (management::Args& args, std::string& text); + Manageable::status_t lookupPublish(management::Args& args, std::string& text); + virtual qpid::management::ManagementObject* GetManagementObject(void) const; + virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); }; - - }} // namespace qpid::acl #endif // QPID_ACL_ACL_H diff --git a/cpp/src/qpid/acl/AclConnectionCounter.cpp b/cpp/src/qpid/acl/AclConnectionCounter.cpp new file mode 100644 index 0000000000..5d4e3c1544 --- /dev/null +++ b/cpp/src/qpid/acl/AclConnectionCounter.cpp @@ -0,0 +1,214 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "AclConnectionCounter.h" +#include "Acl.h" +#include "qpid/broker/Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Mutex.h" +#include <assert.h> +#include <sstream> + +using namespace qpid::sys; + +namespace qpid { +namespace acl { + +// +// This module instantiates a broker::ConnectionObserver and limits client +// connections by counting connections per user name and per client IP address. +// + + +// +// +// +ConnectionCounter::ConnectionCounter(Acl& a, uint32_t nl, uint32_t hl) : + acl(a), nameLimit(nl), hostLimit(hl) {} + +ConnectionCounter::~ConnectionCounter() {} + + +// +// limitCheckLH +// +// Increment the name's count in map and return a comparison against the limit. +// called with dataLock already taken +// +bool ConnectionCounter::limitCheckLH( + connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) { + + bool result(true); + if (theLimit > 0) { + connectCountsMap_t::iterator eRef = theMap.find(theName); + if (eRef != theMap.end()) { + uint32_t count = (uint32_t)(*eRef).second + 1; + (*eRef).second = count; + result = count <= theLimit; + } else { + theMap[theName] = 1; + } + } + return result; +} + + +// +// releaseLH +// +// Decrement the name's count in map. +// called with dataLock already taken +// +void ConnectionCounter::releaseLH( + connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) { + + if (theLimit > 0) { + connectCountsMap_t::iterator eRef = theMap.find(theName); + if (eRef != theMap.end()) { + uint32_t count = (uint32_t) (*eRef).second; + assert (count > 0); + if (1 == count) { + theMap.erase (eRef); + } else { + (*eRef).second = count - 1; + } + } else { + // User had no connections. + QPID_LOG(notice, "ACL ConnectionCounter Connection for '" << theName + << "' not found in connection count pool"); + } + } +} + + +// +// connection - called during Connection's constructor +// +void ConnectionCounter::connection(broker::Connection& connection) { + QPID_LOG(trace, "ACL ConnectionCounter connection IP:" << connection.getMgmtId() + << ", userId:" << connection.getUserId()); + + Mutex::ScopedLock locker(dataLock); + + connectProgressMap[connection.getMgmtId()] = C_CREATED; +} + + +// +// opened - called when first AMQP frame is received over Connection +// +void ConnectionCounter::opened(broker::Connection& connection) { + QPID_LOG(trace, "ACL ConnectionCounter Opened IP:" << connection.getMgmtId() + << ", userId:" << connection.getUserId()); + + Mutex::ScopedLock locker(dataLock); + + const std::string& userName( connection.getUserId()); + const std::string& hostName(getClientHost(connection.getMgmtId())); + + // Bump state from CREATED to OPENED + (void) limitCheckLH(connectProgressMap, connection.getMgmtId(), C_OPENED); + + bool nameOk = limitCheckLH(connectByNameMap, userName, nameLimit); + bool hostOk = limitCheckLH(connectByHostMap, hostName, hostLimit); + + if (!nameOk) { + // User has too many + acl.reportConnectLimit(userName, hostName); + QPID_LOG(notice, "ACL ConnectionCounter User '" << userName + << "' exceeded maximum allowed connections"); + throw Exception( + QPID_MSG("User '" << userName + << "' exceeded maximum allowed connections")); + } + + if (!hostOk) { + // Host has too many + acl.reportConnectLimit(userName, hostName); + QPID_LOG(notice, "ACL ConnectionCounter Client host '" << hostName + << "' exceeded maximum allowed connections"); + throw Exception( + QPID_MSG("Client host '" << hostName + << "' exceeded maximum allowed connections")); + } +} + + +// +// closed - called during Connection's destructor +// +void ConnectionCounter::closed(broker::Connection& connection) { + QPID_LOG(trace, "ACL ConnectionCounter Closed IP:" << connection.getMgmtId() + << ", userId:" << connection.getUserId()); + + Mutex::ScopedLock locker(dataLock); + + connectCountsMap_t::iterator eRef = connectProgressMap.find(connection.getMgmtId()); + if (eRef != connectProgressMap.end()) { + if ((*eRef).second == C_OPENED){ + // Normal case: connection was created and opened. + // Decrement in-use counts + releaseLH(connectByNameMap, + connection.getUserId(), + nameLimit); + + releaseLH(connectByHostMap, + getClientHost(connection.getMgmtId()), + hostLimit); + } else { + // Connection was created but not opened. + // Don't decrement any connection counts. + } + connectProgressMap.erase(eRef); + + } else { + // connection not found in progress map + QPID_LOG(notice, "ACL ConnectionCounter info for '" << connection.getMgmtId() + << "' not found in connection state pool"); + } +} + + +// +// getClientIp - given a connection's mgmtId return the client host part. +// +// TODO: Ideally this would be a method of the connection itself. +// +std::string ConnectionCounter::getClientHost(const std::string mgmtId) +{ + size_t hyphen = mgmtId.find('-'); + if (std::string::npos != hyphen) { + size_t colon = mgmtId.find_last_of(':'); + if (std::string::npos != colon) { + // trailing colon found + return mgmtId.substr(hyphen+1, colon - hyphen - 1); + } else { + // colon not found - use everything after hyphen + return mgmtId.substr(hyphen+1); + } + } + + // no hyphen found - use whole string + assert(false); + return mgmtId; +} + +}} // namespace qpid::ha diff --git a/cpp/src/qpid/acl/AclConnectionCounter.h b/cpp/src/qpid/acl/AclConnectionCounter.h new file mode 100644 index 0000000000..31d11540fd --- /dev/null +++ b/cpp/src/qpid/acl/AclConnectionCounter.h @@ -0,0 +1,81 @@ +#ifndef QPID_ACL_CONNECTIONCOUNTER_H +#define QPID_ACL_CONNECTIONCOUNTER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/ConnectionObserver.h" +#include "qpid/sys/Mutex.h" +#include <boost/iterator/iterator_concepts.hpp> + +#include <map> + +namespace qpid { + +namespace broker { +class Connection; +} + +namespace acl { +class Acl; + + /** + * Terminate client connections when a user tries to create 'too many'. + * Terminate hostIp connections when an IP host tries to create 'too many'. + */ +class ConnectionCounter : public broker::ConnectionObserver +{ +private: + typedef std::map<std::string, uint32_t> connectCountsMap_t; + enum CONNECTION_PROGRESS { C_CREATED=1, C_OPENED=2 }; + + Acl& acl; + uint32_t nameLimit; + uint32_t hostLimit; + qpid::sys::Mutex dataLock; + + connectCountsMap_t connectProgressMap; + connectCountsMap_t connectByNameMap; + connectCountsMap_t connectByHostMap; + + std::string getClientHost(const std::string mgmtId); + + bool limitCheckLH(connectCountsMap_t& theMap, + const std::string& theName, + uint32_t theLimit); + + void releaseLH(connectCountsMap_t& theMap, + const std::string& theName, + uint32_t theLimit); + +public: + ConnectionCounter(Acl& acl, uint32_t nl, uint32_t hl); + ~ConnectionCounter(); + + void connection(broker::Connection& connection); + void opened(broker::Connection& connection); + void closed(broker::Connection& connection); + +}; + +}} // namespace qpid::ha + +#endif /*!QPID_ACL_CONNECTIONCOUNTER_H*/ diff --git a/cpp/src/qpid/acl/AclData.cpp b/cpp/src/qpid/acl/AclData.cpp index 06fc223a73..da7f240a9b 100644 --- a/cpp/src/qpid/acl/AclData.cpp +++ b/cpp/src/qpid/acl/AclData.cpp @@ -24,152 +24,302 @@ namespace qpid { namespace acl { - AclData::AclData():decisionMode(qpid::acl::DENY),transferAcl(false),aclSource("UNKNOWN") + // + // constructor + // + AclData::AclData(): + decisionMode(qpid::acl::DENY), + transferAcl(false), + aclSource("UNKNOWN") { - for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){ + for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) + { actionList[cnt]=0; } - } + + // + // clear + // void AclData::clear () { - for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){ - if (actionList[cnt]){ + for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) + { + if (actionList[cnt]) + { for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++) - delete actionList[cnt][cnt1]; + delete actionList[cnt][cnt1]; } delete[] actionList[cnt]; } - } - bool AclData::matchProp(const std::string & src, const std::string& src1) + + // + // matchProp + // + // Compare a rule's property name with a lookup name, + // The rule's name may contain a trailing '*' to specify a wildcard match. + // + bool AclData::matchProp(const std::string& ruleStr, + const std::string& lookupStr) { - // allow wildcard on the end of strings... - if (src.data()[src.size()-1]=='*') { - return (src.compare(0, src.size()-1, src1, 0,src.size()-1 ) == 0); - } else { - return (src.compare(src1)==0) ; + // allow wildcard on the end of rule strings... + if (ruleStr.data()[ruleStr.size()-1]=='*') + { + return ruleStr.compare(0, + ruleStr.size()-1, + lookupStr, + 0, + ruleStr.size()-1 ) == 0; + } + else + { + return ruleStr.compare(lookupStr) == 0; } } - AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, - const std::string& name, std::map<Property, std::string>* params) { - - QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) - << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " name:" << name - << " with params " << AclHelper::propertyMapToString(params)); - - AclResult aclresult = decisionMode; - if (actionList[action] && actionList[action][objType]) { - AclData::actObjItr itrRule = actionList[action][objType]->find(id); - if (itrRule == actionList[action][objType]->end()) - itrRule = actionList[action][objType]->find("*"); - - if (itrRule != actionList[action][objType]->end()) { - - QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); - - //loop the vector - for (ruleSetItr i = itrRule->second.begin(); i < itrRule->second.end(); i++) { - QPID_LOG(debug, "ACL: checking rule " << i->toString()); - // loop the names looking for match - bool match = true; - for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) { - //match name is exists first - if (pMItr->first == acl::PROP_NAME) { - if (matchProp(pMItr->second, name)){ - QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" - << pMItr->second << "' given in the rule"); - }else{ + + // + // lookup + // + // The ACL main business logic function of matching rules and declaring + // an allow or deny result. + // + AclResult AclData::lookup( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& name, + std::map<Property, std::string>* params) + { + QPID_LOG(debug, "ACL: Lookup for id:" << id + << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) + << " name:" << name + << " with params " << AclHelper::propertyMapToString(params)); + + // A typical log looks like: + // ACL: Lookup for id:bob@QPID action:create objectType:queue name:q2 + // with params { durable=false passive=false autodelete=false + // exclusive=false alternate= policytype= maxqueuesize=0 + // maxqueuecount=0 } + + // Default result is blanket decision mode for the entire ACL list. + AclResult aclresult = decisionMode; + + // Test for lists of rules at the intersection of the Action & Object + if (actionList[action] && actionList[action][objType]) + { + // Find the list of rules for this actorId + AclData::actObjItr itrRule = actionList[action][objType]->find(id); + + // If individual actorId not found then find a rule set for '*'. + if (itrRule == actionList[action][objType]->end()) + itrRule = actionList[action][objType]->find("*"); + + if (itrRule != actionList[action][objType]->end()) + { + // A list of rules exists for this actor/action/object tuple. + // Iterate the rule set to search for a matching rule. + ruleSetItr rsItr = itrRule->second.end(); + for (int cnt = itrRule->second.size(); cnt != 0; cnt--) + { + rsItr--; + + QPID_LOG(debug, "ACL: checking rule " << rsItr->toString()); + + bool match = true; + bool limitChecked = true; + + // Iterate this rule's properties. A 'match' is true when + // all of the rule's properties are found to be satisfied + // in the lookup param list. The lookup may specify things + // (they usually do) that are not in the rule properties but + // these things don't interfere with the rule match. + + for (specPropertyMapItr rulePropMapItr = rsItr->props.begin(); + (rulePropMapItr != rsItr->props.end()) && match; + rulePropMapItr++) + { + // The rule property map's NAME property is given in + // the calling args and not in the param map. + if (rulePropMapItr->first == acl::SPECPROP_NAME) + { + if (matchProp(rulePropMapItr->second, name)) + { + QPID_LOG(debug, "ACL: lookup name '" << name + << "' matched with rule name '" + << rulePropMapItr->second << "'"); + } + else + { + match = false; + QPID_LOG(debug, "ACL: lookup name '" << name + << "' didn't match with rule name '" + << rulePropMapItr->second << "'"); + } + } + else + { + if (params) + { + // The rule's property map non-NAME properties + // found in the lookup's params list. + // In some cases the param's index is not the same + // as rule's index. + propertyMapItr lookupParamItr; + switch (rulePropMapItr->first) + { + case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT: + case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT: + lookupParamItr = params->find(PROP_MAXQUEUECOUNT); + break; + + case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT: + case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT: + lookupParamItr = params->find(PROP_MAXQUEUESIZE); + break; + + default: + lookupParamItr = params->find((Property)rulePropMapItr->first); + break; + }; + + if (lookupParamItr == params->end()) + { + // Now the rule has a specified property + // that does not exist in the caller's + // lookup params list. + // This rule does not match. match = false; - QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" - << pMItr->second << "' given in the rule"); + QPID_LOG(debug, "ACL: lookup parameter map doesn't contain the rule property '" + << AclHelper::getPropertyStr(rulePropMapItr->first) << "'"); } - } else if (params) { //match pMItr against params - propertyMapItr paramItr = params->find(pMItr->first); - if (paramItr == params->end()) { - match = false; - QPID_LOG(debug, "ACL: the given parameter map in lookup doesn't contain the property '" - << AclHelper::getPropertyStr(pMItr->first) << "'"); - }else if ( pMItr->first == acl::PROP_MAXQUEUECOUNT || pMItr->first == acl::PROP_MAXQUEUESIZE ) { - if ( pMItr->first == paramItr->first ) { - - uint64_t aclMax = 0; - uint64_t paramMax = 0; - - try{ - aclMax = boost::lexical_cast<uint64_t>(pMItr->second); - }catch(const boost::bad_lexical_cast&){ - match = false; - QPID_LOG(error,"Error evaluating rule. " << - "Illegal value given in ACL source <" << aclSource << - "> for property '" << - AclHelper::getPropertyStr(pMItr->first) << "' : " << - boost::lexical_cast<std::string>(pMItr->second)); - break; - } - - try{ - paramMax = boost::lexical_cast<uint64_t>(paramItr->second); - }catch(const boost::bad_lexical_cast&){ + else + { + // Now account for the business of rules + // whose property indexes are mismatched. + switch (rulePropMapItr->first) + { + case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT: + case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT: + limitChecked &= + compareIntMax( + rulePropMapItr->first, + boost::lexical_cast<std::string>(rulePropMapItr->second), + boost::lexical_cast<std::string>(lookupParamItr->second)); + break; + + case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT: + case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT: + limitChecked &= + compareIntMin( + rulePropMapItr->first, + boost::lexical_cast<std::string>(rulePropMapItr->second), + boost::lexical_cast<std::string>(lookupParamItr->second)); + break; + + default: + if (matchProp(rulePropMapItr->second, lookupParamItr->second)) + { + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(lookupParamItr->first) + << "," << lookupParamItr->second + << ") given in lookup matched the pair(" + << AclHelper::getPropertyStr(rulePropMapItr->first) << "," + << rulePropMapItr->second + << ") given in the rule"); + } + else + { match = false; - QPID_LOG(error,"Error evaluating rule. " << - "Illegal value given in lookup for property '" << - AclHelper::getPropertyStr(pMItr->first) << "' : " << - boost::lexical_cast<std::string>(paramItr->second)); - break; - } - - QPID_LOG(debug, "ACL: Numeric comparison for property " << - AclHelper::getPropertyStr(paramItr->first) << - " (value given in lookup = " << - boost::lexical_cast<std::string>(paramItr->second) << - ", value give in rule = " << - boost::lexical_cast<std::string>(pMItr->second) << " )"); - - if (( aclMax ) && ( paramMax == 0 || paramMax > aclMax)){ - match = decisionMode == qpid::acl::ALLOW ; - QPID_LOG(debug, "ACL: Limit exceeded and match=" << - (match ? "true": "false") << - " as decision mode is " << AclHelper::getAclResultStr(decisionMode)); + QPID_LOG(debug, "ACL: the pair(" + << AclHelper::getPropertyStr(lookupParamItr->first) + << "," << lookupParamItr->second + << ") given in lookup doesn't match the pair(" + << AclHelper::getPropertyStr(rulePropMapItr->first) + << "," << rulePropMapItr->second + << ") given in the rule"); } - } - }else if (matchProp(pMItr->second, paramItr->second)) { - QPID_LOG(debug, "ACL: the pair(" - << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second - << ") given in lookup matched the pair(" - << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); - } else { - QPID_LOG(debug, "ACL: the pair(" - << AclHelper::getPropertyStr(paramItr->first) << "," << paramItr->second - << ") given in lookup doesn't match the pair(" - << AclHelper::getPropertyStr(pMItr->first) << "," << pMItr->second << ") given in the rule"); - match = false; - } + break; + }; + } + } + else + { + // params don't exist. } } - if (match) + } + if (match) + { + aclresult = rsItr->ruleMode; + if (!limitChecked) { - aclresult = getACLResult(i->logOnly, i->log); - QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); - return aclresult; + // Now a lookup matched all rule properties but one + // of the numeric limit checks has failed. + // Demote allow rules to corresponding deny rules. + switch (aclresult) + { + case acl::ALLOW: + aclresult = acl::DENY; + break; + case acl::ALLOWLOG: + aclresult = acl::DENYLOG; + break; + default: + break; + }; } + QPID_LOG(debug,"ACL: Successful match, the decision is:" + << AclHelper::getAclResultStr(aclresult)); + return aclresult; + } + else + { + // This rule did not match the requested lookup and + // does not contribute to an ACL decision. } } } + else + { + // The Action-Object list has entries but not for this actorId + // nor for *. + } + } + else + { + // The Action-Object list has no entries. + } - QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); - return aclresult; + QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode " + << AclHelper::getAclResultStr(aclresult)); + return aclresult; } - AclResult AclData::lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& /*Exchange*/ name, const std::string& RoutingKey) + + // + // lookup + // + // The ACL main business logic function of matching rules and declaring + // an allow or deny result. + // + AclResult AclData::lookup( + const std::string& id, + const Action& action, + const ObjectType& objType, + const std::string& /*Exchange*/ name, + const std::string& routingKey) { - QPID_LOG(debug, "ACL: Lookup for id:" << id << " action:" << AclHelper::getActionStr((Action) action) - << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) << " exchange name:" << name - << " with routing key " << RoutingKey); + QPID_LOG(debug, "ACL: Lookup for id:" << id + << " action:" << AclHelper::getActionStr((Action) action) + << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType) + << " exchange name:" << name + << " with routing key " << routingKey); AclResult aclresult = decisionMode; @@ -179,83 +329,183 @@ namespace acl { if (itrRule == actionList[action][objType]->end()) itrRule = actionList[action][objType]->find("*"); - if (itrRule != actionList[action][objType]->end() ) { - - QPID_LOG(debug, "ACL: checking the following rules for : " << itrRule->first ); - + if (itrRule != actionList[action][objType]->end() ) + { //loop the vector - for (ruleSetItr i=itrRule->second.begin(); i<itrRule->second.end(); i++) { - QPID_LOG(debug, "ACL: checking rule " << i->toString()); + ruleSetItr rsItr = itrRule->second.end(); + for (int cnt = itrRule->second.size(); cnt != 0; cnt--) + { + rsItr--; + + QPID_LOG(debug, "ACL: checking rule " << rsItr->toString()); // loop the names looking for match bool match =true; - for (propertyMapItr pMItr = i->props.begin(); (pMItr != i->props.end()) && match; pMItr++) + for (specPropertyMapItr pMItr = rsItr->props.begin(); + (pMItr != rsItr->props.end()) && match; + pMItr++) { //match name is exists first - if (pMItr->first == acl::PROP_NAME){ - if (matchProp(pMItr->second, name)){ - QPID_LOG(debug, "ACL: name '" << name << "' matched with name '" - << pMItr->second << "' given in the rule"); + switch (pMItr->first) + { + case acl::SPECPROP_NAME: + if (matchProp(pMItr->second, name)) + { + QPID_LOG(debug, "ACL: lookup exchange name '" + << name << "' matched with rule name '" + << pMItr->second << "'"); - }else{ + } + else + { match= false; - QPID_LOG(debug, "ACL: name '" << name << "' didn't match with name '" - << pMItr->second << "' given in the rule"); - } - }else if (pMItr->first == acl::PROP_ROUTINGKEY){ - if (matchProp(pMItr->second, RoutingKey)){ - QPID_LOG(debug, "ACL: name '" << name << "' matched with routing_key '" - << pMItr->second << "' given in the rule"); - }else{ + QPID_LOG(debug, "ACL: lookup exchange name '" + << name << "' did not match with rule name '" + << pMItr->second << "'"); + } + break; + + case acl::SPECPROP_ROUTINGKEY: + if (matchProp(pMItr->second, routingKey)) + { + QPID_LOG(debug, "ACL: lookup key name '" + << routingKey << "' matched with rule routing key '" + << pMItr->second << "'"); + } + else + { match= false; - QPID_LOG(debug, "ACL: name '" << name << "' didn't match with routing_key '" - << pMItr->second << "' given in the rule"); - } - } + QPID_LOG(debug, "ACL: lookup key name '" + << routingKey << "' did not match with rule routing key '" + << pMItr->second << "'"); + } + break; + + default: + // Don't care + break; + }; } if (match){ - aclresult = getACLResult(i->logOnly, i->log); - QPID_LOG(debug,"Successful match, the decision is:" << AclHelper::getAclResultStr(aclresult)); + aclresult = rsItr->ruleMode; + QPID_LOG(debug,"ACL: Successful match, the decision is:" + << AclHelper::getAclResultStr(aclresult)); return aclresult; } } } } - QPID_LOG(debug,"No successful match, defaulting to the decision mode " << AclHelper::getAclResultStr(aclresult)); + QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode " + << AclHelper::getAclResultStr(aclresult)); return aclresult; } - AclResult AclData::getACLResult(bool logOnly, bool log) + // + // + // + AclData::~AclData() + { + clear(); + } + + + // + // Limit check a MAX int limit + // + bool AclData::compareIntMax(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue) { - switch (decisionMode) + uint64_t aclMax (0); + uint64_t paramMax (0); + + try { - case qpid::acl::ALLOWLOG: - case qpid::acl::ALLOW: - if (logOnly) return qpid::acl::ALLOWLOG; - if (log) - return qpid::acl::DENYLOG; - else - return qpid::acl::DENY; + aclMax = boost::lexical_cast<uint64_t>(theAclValue); + } + catch(const boost::bad_lexical_cast&) + { + assert (false); + return false; + } + + try + { + paramMax = boost::lexical_cast<uint64_t>(theLookupValue); + } + catch(const boost::bad_lexical_cast&) + { + QPID_LOG(error,"ACL: Error evaluating rule. " + << "Illegal value given in lookup for property '" + << AclHelper::getPropertyStr(theProperty) + << "' : " << theLookupValue); + return false; + } + QPID_LOG(debug, "ACL: Numeric greater-than comparison for property " + << AclHelper::getPropertyStr(theProperty) + << " (value given in lookup = " << theLookupValue + << ", value give in rule = " << theAclValue << " )"); - case qpid::acl::DENYLOG: - case qpid::acl::DENY: - if (logOnly) return qpid::acl::DENYLOG; - if (log) - return qpid::acl::ALLOWLOG; - else - return qpid::acl::ALLOW; + if (( aclMax ) && ( paramMax == 0 || paramMax > aclMax)) + { + QPID_LOG(debug, "ACL: Max limit exceeded for property '" + << AclHelper::getPropertyStr(theProperty) << "'"); + return false; } - QPID_LOG(error, "ACL Decision Failed, setting DENY"); - return qpid::acl::DENY; + return true; } - AclData::~AclData() + + // + // limit check a MIN int limit + // + bool AclData::compareIntMin(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue) { - clear(); + uint64_t aclMin (0); + uint64_t paramMin (0); + + try + { + aclMin = boost::lexical_cast<uint64_t>(theAclValue); + } + catch(const boost::bad_lexical_cast&) + { + assert (false); + return false; + } + + try + { + paramMin = boost::lexical_cast<uint64_t>(theLookupValue); + } + catch(const boost::bad_lexical_cast&) + { + QPID_LOG(error,"ACL: Error evaluating rule. " + << "Illegal value given in lookup for property '" + << AclHelper::getPropertyStr(theProperty) + << "' : " << theLookupValue); + return false; + } + + QPID_LOG(debug, "ACL: Numeric less-than comparison for property " + << AclHelper::getPropertyStr(theProperty) + << " (value given in lookup = " << theLookupValue + << ", value give in rule = " << theAclValue << " )"); + + if (( aclMin ) && ( paramMin == 0 || paramMin < aclMin)) + { + QPID_LOG(debug, "ACL: Min limit exceeded for property '" + << AclHelper::getPropertyStr(theProperty) << "'"); + return false; + } + + return true; } -}} +}} diff --git a/cpp/src/qpid/acl/AclData.h b/cpp/src/qpid/acl/AclData.h index 81125fdcbc..1c1cb3e9c6 100644 --- a/cpp/src/qpid/acl/AclData.h +++ b/cpp/src/qpid/acl/AclData.h @@ -33,50 +33,91 @@ class AclData { public: typedef std::map<qpid::acl::Property, std::string> propertyMap; - typedef propertyMap::const_iterator propertyMapItr; + typedef propertyMap::const_iterator propertyMapItr; + + typedef std::map<qpid::acl::SpecProperty, std::string> specPropertyMap; + typedef specPropertyMap::const_iterator specPropertyMapItr; + + // + // rule + // + // Created by AclReader and stored in a ruleSet vector for subsequent + // run-time lookup matching and allow/deny decisions. + // RuleSet vectors are indexed by Action-Object-actorId so these + // attributes are not part of a rule. + // A single ACL file entry may create many rule entries in + // many ruleset vectors. + // struct rule { - bool log; - bool logOnly; // this is a rule is to log only + int rawRuleNum; // rule number in ACL file + qpid::acl::AclResult ruleMode; // combined allow/deny log/nolog + specPropertyMap props; // - // key value map - //?? - propertyMap props; - - rule (propertyMap& p):log(false),logOnly(false),props(p) {}; + rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) : + rawRuleNum(ruleNum), + ruleMode(res), + props(p) + {}; std::string toString () const { std::ostringstream ruleStr; - ruleStr << "[log=" << log << ", logOnly=" << logOnly << " props{"; - for (propertyMapItr pMItr = props.begin(); pMItr != props.end(); pMItr++) { - ruleStr << " " << AclHelper::getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + ruleStr << "[rule " << rawRuleNum + << " ruleMode = " << AclHelper::getAclResultStr(ruleMode) + << " props{"; + for (specPropertyMapItr pMItr = props.begin(); + pMItr != props.end(); + pMItr++) { + ruleStr << " " + << AclHelper::getPropertyStr((SpecProperty) pMItr-> first) + << "=" << pMItr->second; } ruleStr << " }]"; return ruleStr.str(); } }; - typedef std::vector<rule> ruleSet; - typedef ruleSet::const_iterator ruleSetItr; - typedef std::map<std::string, ruleSet > actionObject; // user - typedef actionObject::iterator actObjItr; - typedef actionObject* aclAction; - // Action*[] -> Object*[] -> map<user -> set<Rule> > - aclAction* actionList[qpid::acl::ACTIONSIZE]; - qpid::acl::AclResult decisionMode; // determines if the rule set is a deny or allow mode. - bool transferAcl; - std::string aclSource; + typedef std::vector<rule> ruleSet; + typedef ruleSet::const_iterator ruleSetItr; + typedef std::map<std::string, ruleSet > actionObject; // user + typedef actionObject::iterator actObjItr; + typedef actionObject* aclAction; - AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& name, std::map<Property, std::string>* params=0); - AclResult lookup(const std::string& id, const Action& action, const ObjectType& objType, const std::string& ExchangeName, const std::string& RoutingKey); - AclResult getACLResult(bool logOnly, bool log); + // Action*[] -> Object*[] -> map<user -> set<Rule> > + aclAction* actionList[qpid::acl::ACTIONSIZE]; + qpid::acl::AclResult decisionMode; // allow/deny[-log] if no matching rule found + bool transferAcl; + std::string aclSource; + + AclResult lookup( + const std::string& id, // actor id + const Action& action, + const ObjectType& objType, + const std::string& name, // object name + std::map<Property, std::string>* params=0); + + AclResult lookup( + const std::string& id, // actor id + const Action& action, + const ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey); bool matchProp(const std::string & src, const std::string& src1); void clear (); AclData(); virtual ~AclData(); + +private: + bool compareIntMax(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue); + + bool compareIntMin(const qpid::acl::SpecProperty theProperty, + const std::string theAclValue, + const std::string theLookupValue); }; }} // namespace qpid::acl diff --git a/cpp/src/qpid/acl/AclPlugin.cpp b/cpp/src/qpid/acl/AclPlugin.cpp index d611797c49..6c18cd2749 100644 --- a/cpp/src/qpid/acl/AclPlugin.cpp +++ b/cpp/src/qpid/acl/AclPlugin.cpp @@ -40,7 +40,9 @@ struct AclOptions : public Options { AclOptions(AclValues& v) : Options("ACL Options"), values(v) { addOptions() - ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir"); + ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir") + ("acl-max-connect-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user") + ("acl-max-connect-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address"); } }; diff --git a/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp index 74358a20c1..80debf1bd1 100644 --- a/cpp/src/qpid/acl/AclReader.cpp +++ b/cpp/src/qpid/acl/AclReader.cpp @@ -49,7 +49,7 @@ namespace acl { objStatus = ALL; } - bool AclReader::aclRule::addProperty(const Property p, const std::string v) { + bool AclReader::aclRule::addProperty(const SpecProperty p, const std::string v) { return props.insert(propNvPair(p, v)).second; } @@ -85,146 +85,108 @@ namespace acl { void AclReader::loadDecisionData(boost::shared_ptr<AclData> d) { d->clear(); - QPID_LOG(debug, "ACL Load Rules"); - int cnt = rules.size(); + QPID_LOG(debug, "ACL: Load Rules"); bool foundmode = false; - for (rlCitr i = rules.end(); cnt; cnt--) { + rlCitr i = rules.end(); + for (int cnt = rules.size(); cnt; cnt--) { i--; - QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) + QPID_LOG(debug, "ACL: Processing " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1 && (*((*i)->names.begin())).compare("*") == 0) { d->decisionMode = (*i)->res; - QPID_LOG(debug, "ACL FoundMode " + QPID_LOG(debug, "ACL: FoundMode " << AclHelper::getAclResultStr(d->decisionMode)); foundmode = true; } else { - AclData::rule rule((*i)->props); - bool addrule = true; - - switch ((*i)->res) { - case qpid::acl::ALLOWLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::ALLOW || - d->decisionMode == qpid::acl::ALLOWLOG) - rule.logOnly = true; - break; - case qpid::acl::ALLOW: - if (d->decisionMode == qpid::acl::ALLOW || - d->decisionMode == qpid::acl::ALLOWLOG) - addrule = false; - break; - case qpid::acl::DENYLOG: - rule.log = true; - if (d->decisionMode == qpid::acl::DENY || - d->decisionMode == qpid::acl::DENYLOG) - rule.logOnly = true; - break; - case qpid::acl::DENY: - if (d->decisionMode == qpid::acl::DENY || - d->decisionMode == qpid::acl::DENYLOG) - addrule = false; - break; - default: - throw Exception("Invalid ACL Result loading rules."); - } + AclData::rule rule(cnt, (*i)->res, (*i)->props); // Action -> Object -> map<user -> set<Rule> > - if (addrule) { - std::ostringstream actionstr; - for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); - acnt < acl::ACTIONSIZE; - (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) { - - if (acnt == acl::ACT_PUBLISH) - d->transferAcl = true; // we have transfer ACL - - actionstr << AclHelper::getActionStr((Action) acnt) << ","; - - //find the Action, create if not exist - if (d->actionList[acnt] == NULL) { - d->actionList[acnt] = - new AclData::aclAction[qpid::acl::OBJECTSIZE]; - for (int j = 0; j < qpid::acl::OBJECTSIZE; j++) - d->actionList[acnt][j] = NULL; - } - - // optimize this loop to limit to valid options only!! - for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 - : (*i)->object); - ocnt < acl::OBJECTSIZE; - (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { - - //find the Object, create if not exist - if (d->actionList[acnt][ocnt] == NULL) - d->actionList[acnt][ocnt] = - new AclData::actionObject; - - // add users and Rule to object set - bool allNames = false; - // check to see if names.begin is '*' - if ((*(*i)->names.begin()).compare("*") == 0) - allNames = true; - - for (nsCitr itr = (allNames ? names.begin() - : (*i)->names.begin()); - itr != (allNames ? names.end() : (*i)->names.end()); - itr++) { - - AclData::actObjItr itrRule = - d->actionList[acnt][ocnt]->find(*itr); - - if (itrRule == d->actionList[acnt][ocnt]->end()) { - AclData::ruleSet rSet; - rSet.push_back(rule); - d->actionList[acnt][ocnt]->insert - (make_pair(std::string(*itr), rSet)); - } else { - // TODO add code to check for dead rules - // allow peter create queue name=tmp <-- dead rule!! - // allow peter create queue - - itrRule->second.push_back(rule); - } - } - - } + std::ostringstream actionstr; + for (int acnt = ((*i)->actionAll ? 0 : (*i)->action); + acnt < acl::ACTIONSIZE; + (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) { + + if (acnt == acl::ACT_PUBLISH) + d->transferAcl = true; // we have transfer ACL + + actionstr << AclHelper::getActionStr((Action) acnt) << ","; + + //find the Action, create if not exist + if (d->actionList[acnt] == NULL) { + d->actionList[acnt] = + new AclData::aclAction[qpid::acl::OBJECTSIZE]; + for (int j = 0; j < qpid::acl::OBJECTSIZE; j++) + d->actionList[acnt][j] = NULL; } - std::ostringstream objstr; - for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object); + // TODO: optimize this loop to limit to valid options only!! + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 + : (*i)->object); ocnt < acl::OBJECTSIZE; - (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { - objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ","; + (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + + //find the Object, create if not exist + if (d->actionList[acnt][ocnt] == NULL) + d->actionList[acnt][ocnt] = + new AclData::actionObject; + + // add users and Rule to object set + bool allNames = false; + // check to see if names.begin is '*' + if ((*(*i)->names.begin()).compare("*") == 0) + allNames = true; + + for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); + itr != (allNames ? names.end() : (*i)->names.end()); + itr++) { + AclData::actObjItr itrRule = + d->actionList[acnt][ocnt]->find(*itr); + + if (itrRule == d->actionList[acnt][ocnt]->end()) { + AclData::ruleSet rSet; + rSet.push_back(rule); + d->actionList[acnt][ocnt]->insert + (make_pair(std::string(*itr), rSet)); + } else { + // TODO add code to check for dead rules + // allow peter create queue name=tmp <-- dead rule!! + // allow peter create queue + + itrRule->second.push_back(rule); + } + } } + } - bool allNames = ((*(*i)->names.begin()).compare("*") == 0); - std::ostringstream userstr; - for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); - itr != (allNames ? names.end() : (*i)->names.end()); - itr++) { - userstr << *itr << ","; - } + std::ostringstream objstr; + for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object); + ocnt < acl::OBJECTSIZE; + (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) { + objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ","; + } - QPID_LOG(debug, "ACL: Adding actions {" << - actionstr.str().substr(0,actionstr.str().length()-1) - << "} to objects {" << - objstr.str().substr(0,objstr.str().length()-1) - << "} with props " << - AclHelper::propertyMapToString(&rule.props) - << " for users {" << - userstr.str().substr(0,userstr.str().length()-1) - << "}" ); - } else { - QPID_LOG(debug, "ACL Skipping based on Mode:" - << AclHelper::getAclResultStr(d->decisionMode)); + bool allNames = ((*(*i)->names.begin()).compare("*") == 0); + std::ostringstream userstr; + for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin()); + itr != (allNames ? names.end() : (*i)->names.end()); + itr++) { + userstr << *itr << ","; } - } + QPID_LOG(debug, "ACL: Adding actions {" << + actionstr.str().substr(0,actionstr.str().length()-1) + << "} to objects {" << + objstr.str().substr(0,objstr.str().length()-1) + << "} with props " << + AclHelper::propertyMapToString(&rule.props) + << " for users {" << + userstr.str().substr(0,userstr.str().length()-1) + << "}" ); + } } - } @@ -277,7 +239,7 @@ namespace acl { } ifs.close(); if (err) return -3; - QPID_LOG(notice, "Read ACL file \"" << fn << "\""); + QPID_LOG(notice, "ACL: Read file \"" << fn << "\""); } catch (const std::exception& e) { errorStream << "Unable to read ACL file \"" << fn << "\": " << e.what(); ifs.close(); @@ -410,8 +372,8 @@ namespace acl { // Debug aid void AclReader::printNames() const { - QPID_LOG(debug, "Group list: " << groups.size() << " groups found:" ); - std::string tmp; + QPID_LOG(debug, "ACL: Group list: " << groups.size() << " groups found:" ); + std::string tmp("ACL: "); for (gmCitr i=groups.begin(); i!= groups.end(); i++) { tmp += " \""; tmp += i->first; @@ -421,10 +383,10 @@ namespace acl { tmp += *j; } QPID_LOG(debug, tmp); - tmp.clear(); + tmp = "ACL: "; } - QPID_LOG(debug, "Name list: " << names.size() << " names found:" ); - tmp.clear(); + QPID_LOG(debug, "ACL: name list: " << names.size() << " names found:" ); + tmp = "ACL: "; for (nsCitr k=names.begin(); k!=names.end(); k++) { tmp += " "; tmp += *k; @@ -501,9 +463,9 @@ namespace acl { << propNvp.first << "\". (Must be name=value)"; return false; } - Property prop; + SpecProperty prop; try { - prop = AclHelper::getProperty(propNvp.first); + prop = AclHelper::getSpecProperty(propNvp.first); } catch (...) { errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber << ", Unknown property \"" << propNvp.first << "\"."; @@ -532,10 +494,10 @@ namespace acl { // Debug aid void AclReader::printRules() const { - QPID_LOG(debug, "Rule list: " << rules.size() << " ACL rules found:"); - int cnt = 0; + QPID_LOG(debug, "ACL: Rule list: " << rules.size() << " ACL rules found:"); + int cnt = 1; for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) { - QPID_LOG(debug, " " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); + QPID_LOG(debug, "ACL: " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString()); } } diff --git a/cpp/src/qpid/acl/AclReader.h b/cpp/src/qpid/acl/AclReader.h index 62c6f38f37..730013f4ed 100644 --- a/cpp/src/qpid/acl/AclReader.h +++ b/cpp/src/qpid/acl/AclReader.h @@ -33,65 +33,71 @@ namespace qpid { namespace acl { class AclReader { - typedef std::set<std::string> nameSet; - typedef nameSet::const_iterator nsCitr; - typedef boost::shared_ptr<nameSet> nameSetPtr; - - typedef std::pair<std::string, nameSetPtr> groupPair; - typedef std::map<std::string, nameSetPtr> groupMap; - typedef groupMap::const_iterator gmCitr; - typedef std::pair<gmCitr, bool> gmRes; - - typedef std::pair<Property, std::string> propNvPair; - typedef std::map<Property, std::string> propMap; - typedef propMap::const_iterator pmCitr; - + typedef std::set<std::string> nameSet; + typedef nameSet::const_iterator nsCitr; + typedef boost::shared_ptr<nameSet> nameSetPtr; + + typedef std::pair<std::string, nameSetPtr> groupPair; + typedef std::map<std::string, nameSetPtr> groupMap; + typedef groupMap::const_iterator gmCitr; + typedef std::pair<gmCitr, bool> gmRes; + + typedef std::pair<SpecProperty, std::string> propNvPair; + typedef std::map<SpecProperty, std::string> propMap; + typedef propMap::const_iterator pmCitr; + + // + // aclRule + // + // A temporary rule created during ACL file processing. + // class aclRule { public: enum objectStatus {NONE, VALUE, ALL}; - AclResult res; - nameSet names; - bool actionAll; // True if action is set to keyword "all" - Action action; // Ignored if action is set to keyword "all" - objectStatus objStatus; - ObjectType object; // Ignored for all status values except VALUE - propMap props; + + AclResult res; + nameSet names; + bool actionAll; // True if action is set to keyword "all" + Action action; // Ignored if action is set to keyword "all" + objectStatus objStatus; + ObjectType object; // Ignored for all status values except VALUE + propMap props; public: aclRule(const AclResult r, const std::string n, const groupMap& groups); // action = "all" aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a); void setObjectType(const ObjectType o); void setObjectTypeAll(); - bool addProperty(const Property p, const std::string v); + bool addProperty(const SpecProperty p, const std::string v); bool validate(const AclHelper::objectMapPtr& validationMap); std::string toString(); // debug aid private: void processName(const std::string& name, const groupMap& groups); }; - typedef boost::shared_ptr<aclRule> aclRulePtr; - typedef std::vector<aclRulePtr> ruleList; - typedef ruleList::const_iterator rlCitr; + typedef boost::shared_ptr<aclRule> aclRulePtr; + typedef std::vector<aclRulePtr> ruleList; + typedef ruleList::const_iterator rlCitr; - typedef std::vector<std::string> tokList; - typedef tokList::const_iterator tlCitr; + typedef std::vector<std::string> tokList; + typedef tokList::const_iterator tlCitr; - typedef std::set<std::string> keywordSet; - typedef keywordSet::const_iterator ksCitr; + typedef std::set<std::string> keywordSet; + typedef keywordSet::const_iterator ksCitr; typedef std::pair<std::string, std::string> nvPair; // Name-Value pair - std::string fileName; - int lineNumber; - bool contFlag; - std::string groupName; - nameSet names; - groupMap groups; - ruleList rules; + std::string fileName; + int lineNumber; + bool contFlag; + std::string groupName; + nameSet names; + groupMap groups; + ruleList rules; AclHelper::objectMapPtr validationMap; - std::ostringstream errorStream; + std::ostringstream errorStream; public: AclReader(); virtual ~AclReader(); - int read(const std::string& fn, boost::shared_ptr<AclData> d); + int read(const std::string& fn, boost::shared_ptr<AclData> d); // return=0 for success std::string getError(); private: diff --git a/cpp/src/qpid/acl/AclValidator.cpp b/cpp/src/qpid/acl/AclValidator.cpp index d5a00b005b..49bb65db4b 100644 --- a/cpp/src/qpid/acl/AclValidator.cpp +++ b/cpp/src/qpid/acl/AclValidator.cpp @@ -29,7 +29,7 @@ namespace qpid { namespace acl { - AclValidator::IntPropertyType::IntPropertyType(int64_t i,int64_t j) : min(i), max(j){ + AclValidator::IntPropertyType::IntPropertyType(int64_t i,int64_t j) : min(i), max(j){ } bool AclValidator::IntPropertyType::validate(const std::string& val) { @@ -49,12 +49,12 @@ namespace acl { } std::string AclValidator::IntPropertyType::allowedValues() { - return "values should be between " + + return "values should be between " + boost::lexical_cast<std::string>(min) + " and " + boost::lexical_cast<std::string>(max); } - AclValidator::EnumPropertyType::EnumPropertyType(std::vector<std::string>& allowed): values(allowed){ + AclValidator::EnumPropertyType::EnumPropertyType(std::vector<std::string>& allowed): values(allowed){ } bool AclValidator::EnumPropertyType::validate(const std::string& val) { @@ -78,24 +78,27 @@ namespace acl { } AclValidator::AclValidator(){ - validators.insert(Validator(acl::PROP_MAXQUEUESIZE, - boost::shared_ptr<PropertyType>( - new IntPropertyType(0,std::numeric_limits<int64_t>::max())) - ) - ); - - validators.insert(Validator(acl::PROP_MAXQUEUECOUNT, - boost::shared_ptr<PropertyType>( - new IntPropertyType(0,std::numeric_limits<int64_t>::max())) - ) - ); + validators.insert(Validator(acl::SPECPROP_MAXQUEUESIZELOWERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); + + validators.insert(Validator(acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); + + validators.insert(Validator(acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); + + validators.insert(Validator(acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT, + boost::shared_ptr<PropertyType>( + new IntPropertyType(0,std::numeric_limits<int64_t>::max())))); std::string policyTypes[] = {"ring", "ring_strict", "flow_to_disk", "reject"}; std::vector<std::string> v(policyTypes, policyTypes + sizeof(policyTypes) / sizeof(std::string)); - validators.insert(Validator(acl::PROP_POLICYTYPE, - boost::shared_ptr<PropertyType>(new EnumPropertyType(v)) - ) - ); + validators.insert(Validator(acl::SPECPROP_POLICYTYPE, + boost::shared_ptr<PropertyType>( + new EnumPropertyType(v)))); } @@ -114,9 +117,9 @@ namespace acl { if (d->actionList[cnt][cnt1]){ std::for_each(d->actionList[cnt][cnt1]->begin(), - d->actionList[cnt][cnt1]->end(), - boost::bind(&AclValidator::validateRuleSet, this, _1)); - }//if + d->actionList[cnt][cnt1]->end(), + boost::bind(&AclValidator::validateRuleSet, this, _1)); + }//if }//for }//if }//for @@ -125,25 +128,29 @@ namespace acl { void AclValidator::validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules){ std::for_each(rules.second.begin(), rules.second.end(), - boost::bind(&AclValidator::validateRule, this, _1)); + boost::bind(&AclValidator::validateRule, this, _1)); } void AclValidator::validateRule(qpid::acl::AclData::rule& rule){ std::for_each(rule.props.begin(), rule.props.end(), - boost::bind(&AclValidator::validateProperty, this, _1)); + boost::bind(&AclValidator::validateProperty, this, _1)); } - void AclValidator::validateProperty(std::pair<const qpid::acl::Property, std::string>& prop){ + void AclValidator::validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop){ ValidatorItr itr = validators.find(prop.first); if (itr != validators.end()){ - QPID_LOG(debug,"Found validator for property " << itr->second->allowedValues()); + QPID_LOG(debug,"ACL: Found validator for property '" << acl::AclHelper::getPropertyStr(itr->first) + << "'. " << itr->second->allowedValues()); if (!itr->second->validate(prop.second)){ - throw Exception( prop.second + " is not a valid value for '" + + QPID_LOG(debug, "ACL: Property failed validation. '" << prop.second << "' is not a valid value for '" + << AclHelper::getPropertyStr(prop.first) << "'"); + + throw Exception( prop.second + " is not a valid value for '" + AclHelper::getPropertyStr(prop.first) + "', " + itr->second->allowedValues()); - } + } } } diff --git a/cpp/src/qpid/acl/AclValidator.h b/cpp/src/qpid/acl/AclValidator.h index 966e5d326b..f85c241b06 100644 --- a/cpp/src/qpid/acl/AclValidator.h +++ b/cpp/src/qpid/acl/AclValidator.h @@ -33,18 +33,18 @@ namespace acl { class AclValidator { /* Base Property */ - class PropertyType{ - + class PropertyType{ + public: virtual ~PropertyType(){}; virtual bool validate(const std::string& val)=0; virtual std::string allowedValues()=0; }; - class IntPropertyType : public PropertyType{ + class IntPropertyType : public PropertyType{ int64_t min; int64_t max; - + public: IntPropertyType(int64_t min,int64_t max); virtual ~IntPropertyType (){}; @@ -53,7 +53,7 @@ class AclValidator { }; class EnumPropertyType : public PropertyType{ - std::vector<std::string> values; + std::vector<std::string> values; public: EnumPropertyType(std::vector<std::string>& allowed); @@ -61,23 +61,23 @@ class AclValidator { virtual bool validate(const std::string& val); virtual std::string allowedValues(); }; - - typedef std::pair<acl::Property,boost::shared_ptr<PropertyType> > Validator; - typedef std::map<acl::Property,boost::shared_ptr<PropertyType> > ValidatorMap; + + typedef std::pair<acl::SpecProperty,boost::shared_ptr<PropertyType> > Validator; + typedef std::map<acl::SpecProperty,boost::shared_ptr<PropertyType> > ValidatorMap; typedef ValidatorMap::iterator ValidatorItr; - + ValidatorMap validators; public: void validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules); void validateRule(qpid::acl::AclData::rule& rule); - void validateProperty(std::pair<const qpid::acl::Property, std::string>& prop); - void validate(boost::shared_ptr<AclData> d); + void validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop); + void validate(boost::shared_ptr<AclData> d); AclValidator(); ~AclValidator(); }; - + }} // namespace qpid::acl #endif // QPID_ACL_ACLVALIDATOR_H diff --git a/cpp/src/qpid/acl/management-schema.xml b/cpp/src/qpid/acl/management-schema.xml index 7f48a9be34..19fe37333c 100644 --- a/cpp/src/qpid/acl/management-schema.xml +++ b/cpp/src/qpid/acl/management-schema.xml @@ -23,8 +23,40 @@ <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/> <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/> <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/> + <statistic name="connectionDenyCount" type="count64" unit="connection" desc="Number of connections denied"/> <method name="reloadACLFile" desc="Reload the ACL file"/> + + <!-- + Lookup is a general object lookup + User Name + Action + Object + Object Name + Property Map consisting of <"name" "value"> string pairs. + --> + <method name="Lookup" desc="Lookup: user action object [objectName [propertyMap]]"> + <arg name="userId" dir="I" type="lstr"/> + <arg name="action" dir="I" type="lstr"/> + <arg name="object" dir="I" type="lstr"/> + <arg name="objectName" dir="I" type="lstr"/> + <arg name="propertyMap" dir="I" type="map"/> + <arg name="result" dir="O" type="lstr"/> + </method> + + <!-- + LookupPublish is a specific lookup for a PUBLISH EXCHANGE fastpath + User Name + Exchange Name + Routing Key + --> + <method name="LookupPublish" desc="Lookup PUBLISH EXCHANGE: user exchangeName routingKey"> + <arg name="userId" dir="I" type="lstr"/> + <arg name="exchangeName" dir="I" type="lstr"/> + <arg name="routingKey" dir="I" type="lstr"/> + <arg name="result" dir="O" type="lstr"/> + </method> + </class> <eventArguments> @@ -34,10 +66,12 @@ <arg name="objectType" type="sstr"/> <arg name="reason" type="lstr"/> <arg name="userId" type="sstr"/> + <arg name="clientAddr" type="sstr"/> </eventArguments> <event name="allow" sev="inform" args="userId, action, objectType, objectName, arguments"/> <event name="deny" sev="notice" args="userId, action, objectType, objectName, arguments"/> + <event name="connectionDeny" sev="notice" args="userId, clientAddr"/> <event name="fileLoaded" sev="inform" args="userId"/> <event name="fileLoadFailed" sev="error" args="userId, reason"/> diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/cpp/src/qpid/agent/ManagementAgentImpl.cpp index f183ff8e0c..09b7fa58e9 100644 --- a/cpp/src/qpid/agent/ManagementAgentImpl.cpp +++ b/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -31,9 +31,11 @@ #include <fstream> #include <boost/lexical_cast.hpp> +namespace qpid { +namespace management { + using namespace qpid::client; using namespace qpid::framing; -using namespace qpid::management; using namespace qpid::sys; using namespace std; using std::stringstream; @@ -1260,7 +1262,7 @@ void ManagementAgentImpl::ConnectionThread::run() int totalSleep = 0; do { sys::Mutex::ScopedUnlock _unlock(connLock); - ::sleep(delayMin); + qpid::sys::sleep(delayMin); totalSleep += delayMin; } while (totalSleep < delay && !shutdown); sleeping = false; @@ -1396,8 +1398,11 @@ void ManagementAgentImpl::PublishThread::run() sleepTime = 1; while (totalSleep < agent.getInterval() && !shutdown) { - ::sleep(sleepTime); + qpid::sys::sleep(sleepTime); totalSleep += sleepTime; } } } + +}} + diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h index e32ff266b9..ff9281b6fc 100644 --- a/cpp/src/qpid/broker/AclModule.h +++ b/cpp/src/qpid/broker/AclModule.h @@ -22,6 +22,7 @@ #include "qpid/RefCounted.h" +#include "qpid/Exception.h" #include <boost/shared_ptr.hpp> #include <map> #include <set> @@ -32,17 +33,81 @@ namespace qpid { namespace acl { - enum ObjectType {OBJ_QUEUE, OBJ_EXCHANGE, OBJ_BROKER, OBJ_LINK, - OBJ_METHOD, OBJECTSIZE}; // OBJECTSIZE must be last in list - enum Action {ACT_CONSUME, ACT_PUBLISH, ACT_CREATE, ACT_ACCESS, ACT_BIND, - ACT_UNBIND, ACT_DELETE, ACT_PURGE, ACT_UPDATE, - ACTIONSIZE}; // ACTIONSIZE must be last in list - enum Property {PROP_NAME, PROP_DURABLE, PROP_OWNER, PROP_ROUTINGKEY, - PROP_PASSIVE, PROP_AUTODELETE, PROP_EXCLUSIVE, PROP_TYPE, - PROP_ALTERNATE, PROP_QUEUENAME, PROP_SCHEMAPACKAGE, - PROP_SCHEMACLASS, PROP_POLICYTYPE, PROP_MAXQUEUESIZE, - PROP_MAXQUEUECOUNT}; - enum AclResult {ALLOW, ALLOWLOG, DENY, DENYLOG}; + // Interface enumerations. + // These enumerations define enum lists and implied text strings + // to match. They are used in two areas: + // 1. In the ACL specifications in the ACL file, file parsing, and + // internal rule storage. + // 2. In the authorize interface in the rest of the broker where + // code requests the ACL module to authorize an action. + + // ObjectType shared between ACL spec and ACL authorise interface + enum ObjectType { + OBJ_QUEUE, + OBJ_EXCHANGE, + OBJ_BROKER, + OBJ_LINK, + OBJ_METHOD, + OBJECTSIZE }; // OBJECTSIZE must be last in list + + // Action shared between ACL spec and ACL authorise interface + enum Action { + ACT_CONSUME, + ACT_PUBLISH, + ACT_CREATE, + ACT_ACCESS, + ACT_BIND, + ACT_UNBIND, + ACT_DELETE, + ACT_PURGE, + ACT_UPDATE, + ACTIONSIZE }; // ACTIONSIZE must be last in list + + // Property used in ACL authorize interface + enum Property { + PROP_NAME, + PROP_DURABLE, + PROP_OWNER, + PROP_ROUTINGKEY, + PROP_AUTODELETE, + PROP_EXCLUSIVE, + PROP_TYPE, + PROP_ALTERNATE, + PROP_QUEUENAME, + PROP_SCHEMAPACKAGE, + PROP_SCHEMACLASS, + PROP_POLICYTYPE, + PROP_MAXQUEUESIZE, + PROP_MAXQUEUECOUNT }; + + // Property used in ACL spec file + // Note for properties common to file processing/rule storage and to + // broker rule lookups the identical enum values are used. + enum SpecProperty { + SPECPROP_NAME = PROP_NAME, + SPECPROP_DURABLE = PROP_DURABLE, + SPECPROP_OWNER = PROP_OWNER, + SPECPROP_ROUTINGKEY = PROP_ROUTINGKEY, + SPECPROP_AUTODELETE = PROP_AUTODELETE, + SPECPROP_EXCLUSIVE = PROP_EXCLUSIVE, + SPECPROP_TYPE = PROP_TYPE, + SPECPROP_ALTERNATE = PROP_ALTERNATE, + SPECPROP_QUEUENAME = PROP_QUEUENAME, + SPECPROP_SCHEMAPACKAGE = PROP_SCHEMAPACKAGE, + SPECPROP_SCHEMACLASS = PROP_SCHEMACLASS, + SPECPROP_POLICYTYPE = PROP_POLICYTYPE, + + SPECPROP_MAXQUEUESIZELOWERLIMIT, + SPECPROP_MAXQUEUESIZEUPPERLIMIT, + SPECPROP_MAXQUEUECOUNTLOWERLIMIT, + SPECPROP_MAXQUEUECOUNTUPPERLIMIT }; + +// AclResult shared between ACL spec and ACL authorise interface + enum AclResult { + ALLOW, + ALLOWLOG, + DENY, + DENYLOG }; } // namespace acl @@ -54,14 +119,25 @@ namespace broker { public: - // effienty turn off ACL on message transfer. + // Some ACLs are invoked on every message transfer. + // doTransferAcl pervents time consuming ACL calls on a per-message basis. virtual bool doTransferAcl()=0; - virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& name, + virtual bool authorise( + const std::string& id, + const acl::Action& action, + const acl::ObjectType& objType, + const std::string& name, std::map<acl::Property, std::string>* params=0)=0; - virtual bool authorise(const std::string& id, const acl::Action& action, const acl::ObjectType& objType, const std::string& ExchangeName, - const std::string& RoutingKey)=0; - // create specilied authorise methods for cases that need faster matching as needed. + + virtual bool authorise( + const std::string& id, + const acl::Action& action, + const acl::ObjectType& objType, + const std::string& ExchangeName, + const std::string& RoutingKey)=0; + + // Add specialized authorise() methods as required. virtual ~AclModule() {}; }; @@ -79,7 +155,7 @@ namespace acl { if (str.compare("broker") == 0) return OBJ_BROKER; if (str.compare("link") == 0) return OBJ_LINK; if (str.compare("method") == 0) return OBJ_METHOD; - throw str; + throw qpid::Exception(str); } static inline std::string getObjectTypeStr(const ObjectType o) { switch (o) { @@ -102,7 +178,7 @@ namespace acl { if (str.compare("delete") == 0) return ACT_DELETE; if (str.compare("purge") == 0) return ACT_PURGE; if (str.compare("update") == 0) return ACT_UPDATE; - throw str; + throw qpid::Exception(str); } static inline std::string getActionStr(const Action a) { switch (a) { @@ -124,7 +200,6 @@ namespace acl { if (str.compare("durable") == 0) return PROP_DURABLE; if (str.compare("owner") == 0) return PROP_OWNER; if (str.compare("routingkey") == 0) return PROP_ROUTINGKEY; - if (str.compare("passive") == 0) return PROP_PASSIVE; if (str.compare("autodelete") == 0) return PROP_AUTODELETE; if (str.compare("exclusive") == 0) return PROP_EXCLUSIVE; if (str.compare("type") == 0) return PROP_TYPE; @@ -134,8 +209,8 @@ namespace acl { if (str.compare("schemaclass") == 0) return PROP_SCHEMACLASS; if (str.compare("policytype") == 0) return PROP_POLICYTYPE; if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE; - if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT; - throw str; + if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT; + throw qpid::Exception(str); } static inline std::string getPropertyStr(const Property p) { switch (p) { @@ -143,7 +218,6 @@ namespace acl { case PROP_DURABLE: return "durable"; case PROP_OWNER: return "owner"; case PROP_ROUTINGKEY: return "routingkey"; - case PROP_PASSIVE: return "passive"; case PROP_AUTODELETE: return "autodelete"; case PROP_EXCLUSIVE: return "exclusive"; case PROP_TYPE: return "type"; @@ -153,17 +227,61 @@ namespace acl { case PROP_SCHEMACLASS: return "schemaclass"; case PROP_POLICYTYPE: return "policytype"; case PROP_MAXQUEUESIZE: return "maxqueuesize"; - case PROP_MAXQUEUECOUNT: return "maxqueuecount"; + case PROP_MAXQUEUECOUNT: return "maxqueuecount"; default: assert(false); // should never get here } return ""; } + static inline SpecProperty getSpecProperty(const std::string& str) { + if (str.compare("name") == 0) return SPECPROP_NAME; + if (str.compare("durable") == 0) return SPECPROP_DURABLE; + if (str.compare("owner") == 0) return SPECPROP_OWNER; + if (str.compare("routingkey") == 0) return SPECPROP_ROUTINGKEY; + if (str.compare("autodelete") == 0) return SPECPROP_AUTODELETE; + if (str.compare("exclusive") == 0) return SPECPROP_EXCLUSIVE; + if (str.compare("type") == 0) return SPECPROP_TYPE; + if (str.compare("alternate") == 0) return SPECPROP_ALTERNATE; + if (str.compare("queuename") == 0) return SPECPROP_QUEUENAME; + if (str.compare("schemapackage") == 0) return SPECPROP_SCHEMAPACKAGE; + if (str.compare("schemaclass") == 0) return SPECPROP_SCHEMACLASS; + if (str.compare("policytype") == 0) return SPECPROP_POLICYTYPE; + if (str.compare("queuemaxsizelowerlimit") == 0) return SPECPROP_MAXQUEUESIZELOWERLIMIT; + if (str.compare("queuemaxsizeupperlimit") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT; + if (str.compare("queuemaxcountlowerlimit") == 0) return SPECPROP_MAXQUEUECOUNTLOWERLIMIT; + if (str.compare("queuemaxcountupperlimit") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT; + // Allow old names in ACL file as aliases for newly-named properties + if (str.compare("maxqueuesize") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT; + if (str.compare("maxqueuecount") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT; + throw qpid::Exception(str); + } + static inline std::string getPropertyStr(const SpecProperty p) { + switch (p) { + case SPECPROP_NAME: return "name"; + case SPECPROP_DURABLE: return "durable"; + case SPECPROP_OWNER: return "owner"; + case SPECPROP_ROUTINGKEY: return "routingkey"; + case SPECPROP_AUTODELETE: return "autodelete"; + case SPECPROP_EXCLUSIVE: return "exclusive"; + case SPECPROP_TYPE: return "type"; + case SPECPROP_ALTERNATE: return "alternate"; + case SPECPROP_QUEUENAME: return "queuename"; + case SPECPROP_SCHEMAPACKAGE: return "schemapackage"; + case SPECPROP_SCHEMACLASS: return "schemaclass"; + case SPECPROP_POLICYTYPE: return "policytype"; + case SPECPROP_MAXQUEUESIZELOWERLIMIT: return "queuemaxsizelowerlimit"; + case SPECPROP_MAXQUEUESIZEUPPERLIMIT: return "queuemaxsizeupperlimit"; + case SPECPROP_MAXQUEUECOUNTLOWERLIMIT: return "queuemaxcountlowerlimit"; + case SPECPROP_MAXQUEUECOUNTUPPERLIMIT: return "queuemaxcountupperlimit"; + default: assert(false); // should never get here + } + return ""; + } static inline AclResult getAclResult(const std::string& str) { if (str.compare("allow") == 0) return ALLOW; if (str.compare("allow-log") == 0) return ALLOWLOG; if (str.compare("deny") == 0) return DENY; if (str.compare("deny-log") == 0) return DENYLOG; - throw str; + throw qpid::Exception(str); } static inline std::string getAclResultStr(const AclResult r) { switch (r) { @@ -187,8 +305,11 @@ namespace acl { typedef boost::shared_ptr<objectMap> objectMapPtr; typedef std::map<Property, std::string> propMap; typedef propMap::const_iterator propMapItr; + typedef std::map<SpecProperty, std::string> specPropMap; + typedef specPropMap::const_iterator specPropMapItr; - // This map contains the legal combinations of object/action/properties found in an ACL file + // This map contains the legal combinations of object/action/properties + // found in an ACL file static void loadValidationMap(objectMapPtr& map) { if (!map.get()) return; map->clear(); @@ -199,7 +320,6 @@ namespace acl { propSetPtr p1(new propSet); p1->insert(PROP_TYPE); p1->insert(PROP_ALTERNATE); - p1->insert(PROP_PASSIVE); p1->insert(PROP_DURABLE); propSetPtr p2(new propSet); @@ -224,7 +344,6 @@ namespace acl { propSetPtr p4(new propSet); p4->insert(PROP_ALTERNATE); - p4->insert(PROP_PASSIVE); p4->insert(PROP_DURABLE); p4->insert(PROP_EXCLUSIVE); p4->insert(PROP_AUTODELETE); @@ -260,21 +379,31 @@ namespace acl { map->insert(objectPair(OBJ_METHOD, a4)); } - static std::string propertyMapToString(const std::map<Property, std::string>* params) { + // + // properyMapToString + // + template <typename T> + static std::string propertyMapToString( + const std::map<T, std::string>* params) + { std::ostringstream ss; ss << "{"; if (params) { - for (propMapItr pMItr = params->begin(); pMItr != params->end(); pMItr++) { - ss << " " << getPropertyStr((Property) pMItr-> first) << "=" << pMItr->second; + for (typename std::map<T, std::string>::const_iterator + pMItr = params->begin(); pMItr != params->end(); pMItr++) + { + ss << " " << getPropertyStr((T) pMItr-> first) + << "=" << pMItr->second; } } ss << " }"; return ss.str(); } + }; - + }} // namespace qpid::acl #endif // QPID_ACLMODULE_ACL_H diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp index 9a1f4be468..5b531e4636 100644 --- a/cpp/src/qpid/broker/Bridge.cpp +++ b/cpp/src/qpid/broker/Bridge.cpp @@ -62,7 +62,7 @@ Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l, InitializeCallback init) : link(_link), id(_id), args(_args), mgmtObject(0), listener(l), name(Uuid(true).str()), queueName("qpid.bridge_queue_"), persistenceId(0), - initialize(init) + initialize(init), detached(false) { std::stringstream title; title << id << "_" << name; @@ -85,11 +85,14 @@ Bridge::~Bridge() void Bridge::create(Connection& c) { + detached = false; // Reset detached in case we are recovering. connState = &c; conn = &c; FieldTable options; if (args.i_sync) options.setInt("qpid.sync_frequency", args.i_sync); SessionHandler& sessionHandler = c.getChannel(id); + sessionHandler.setDetachedCallback( + boost::bind(&Bridge::sessionDetached, shared_from_this())); if (args.i_srcIsLocal) { if (args.i_dynamic) throw Exception("Dynamic routing not supported for push routes"); @@ -179,12 +182,6 @@ void Bridge::destroy() listener(this); } -bool Bridge::isSessionReady() const -{ - SessionHandler& sessionHandler = conn->getChannel(id); - return sessionHandler.ready(); -} - void Bridge::setPersistenceId(uint64_t pId) const { persistenceId = pId; @@ -336,4 +333,8 @@ const string& Bridge::getLocalTag() const return link->getBroker()->getFederationTag(); } +void Bridge::sessionDetached() { + detached = true; +} + }} diff --git a/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h index b849b11ba8..32b9fd1781 100644 --- a/cpp/src/qpid/broker/Bridge.h +++ b/cpp/src/qpid/broker/Bridge.h @@ -33,6 +33,7 @@ #include "qmf/org/apache/qpid/broker/Bridge.h" #include <boost/function.hpp> +#include <boost/enable_shared_from_this.hpp> #include <memory> namespace qpid { @@ -44,7 +45,10 @@ class Link; class LinkRegistry; class SessionHandler; -class Bridge : public PersistableConfig, public management::Manageable, public Exchange::DynamicBridge +class Bridge : public PersistableConfig, + public management::Manageable, + public Exchange::DynamicBridge, + public boost::enable_shared_from_this<Bridge> { public: typedef boost::shared_ptr<Bridge> shared_ptr; @@ -63,7 +67,7 @@ public: void destroy(); bool isDurable() { return args.i_durable; } - bool isSessionReady() const; + bool isDetached() const { return detached; } management::ManagementObject* GetManagementObject() const; management::Manageable::status_t ManagementMethod(uint32_t methodId, @@ -90,6 +94,9 @@ public: const qmf::org::apache::qpid::broker::ArgsLinkBridge& getArgs() { return args; } private: + // Callback when the bridge's session is detached. + void sessionDetached(); + struct PushHandler : framing::FrameHandler { PushHandler(Connection* c) { conn = c; } void handle(framing::AMQFrame& frame); @@ -112,7 +119,7 @@ private: ConnectionState* connState; Connection* conn; InitializeCallback initialize; - + bool detached; // Set when session is detached. bool resetProxy(); }; diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp index 0fd31580f6..f20cce18a2 100644 --- a/cpp/src/qpid/broker/Broker.cpp +++ b/cpp/src/qpid/broker/Broker.cpp @@ -111,6 +111,7 @@ Broker::Options::Options(const std::string& name) : maxConnections(500), connectionBacklog(10), enableMgmt(1), + mgmtPublish(1), mgmtPubInterval(10), queueCleanInterval(60*10),//10 minutes auth(SaslAuthenticator::available()), @@ -148,6 +149,7 @@ Broker::Options::Options(const std::string& name) : ("max-connections", optValue(maxConnections, "N"), "Sets the maximum allowed connections") ("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket") ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management") + ("mgmt-publish", optValue(mgmtPublish,"yes|no"), "Enable Publish of Management Data ('no' implies query-only)") ("mgmt-qmf2", optValue(qmf2Support,"yes|no"), "Enable broadcast of management information over QMF v2") ("mgmt-qmf1", optValue(qmf1Support,"yes|no"), "Enable broadcast of management information over QMF v1") // FIXME aconway 2012-02-13: consistent treatment of values in SECONDS @@ -213,7 +215,7 @@ Broker::Broker(const Broker::Options& conf) : try { if (conf.enableMgmt) { QPID_LOG(info, "Management enabled"); - managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(), + managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(), conf.mgmtPublish, conf.mgmtPubInterval, this, conf.workerThreads + 3); managementAgent->setName("apache.org", "qpidd"); _qmf::Package packageInitializer(managementAgent.get()); @@ -228,6 +230,7 @@ Broker::Broker(const Broker::Options& conf) : mgmtObject->set_maxConns(conf.maxConnections); mgmtObject->set_connBacklog(conf.connectionBacklog); mgmtObject->set_mgmtPubInterval(conf.mgmtPubInterval); + mgmtObject->set_mgmtPublish(conf.mgmtPublish); mgmtObject->set_version(qpid::version); if (dataDir.isEnabled()) mgmtObject->set_dataDir(dataDir.getPath()); @@ -885,7 +888,6 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( if (acl) { std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _FALSE)); params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); params.insert(make_pair(acl::PROP_EXCLUSIVE, owner ? _TRUE : _FALSE)); params.insert(make_pair(acl::PROP_AUTODELETE, autodelete ? _TRUE : _FALSE)); @@ -956,7 +958,6 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange( std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_TYPE, type)); params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _FALSE)); params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_EXCHANGE,name,¶ms) ) throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << userId)); diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h index cff38eecdd..135b9340f9 100644 --- a/cpp/src/qpid/broker/Broker.h +++ b/cpp/src/qpid/broker/Broker.h @@ -106,6 +106,7 @@ public: int maxConnections; int connectionBacklog; bool enableMgmt; + bool mgmtPublish; uint16_t mgmtPubInterval; uint16_t queueCleanInterval; bool auth; @@ -206,7 +207,7 @@ public: ConsumerFactories consumerFactories; public: - virtual ~Broker(); + QPID_BROKER_EXTERN virtual ~Broker(); QPID_BROKER_EXTERN Broker(const Options& configuration); static QPID_BROKER_EXTERN boost::intrusive_ptr<Broker> create(const Options& configuration); @@ -218,16 +219,16 @@ public: * port, which will be different if the configured port is * 0. */ - virtual uint16_t getPort(const std::string& name) const; + QPID_BROKER_EXTERN virtual uint16_t getPort(const std::string& name) const; /** * Run the broker. Implements Runnable::run() so the broker * can be run in a separate thread. */ - virtual void run(); + QPID_BROKER_EXTERN virtual void run(); /** Shut down the broker */ - virtual void shutdown(); + QPID_BROKER_EXTERN virtual void shutdown(); QPID_BROKER_EXTERN void setStore (boost::shared_ptr<MessageStore>& store); void setAsyncStore(boost::shared_ptr<AsyncStore>& asyncStore); @@ -248,14 +249,14 @@ public: SessionManager& getSessionManager() { return sessionManager; } const std::string& getFederationTag() const { return federationTag; } - management::ManagementObject* GetManagementObject (void) const; - management::Manageable* GetVhostObject (void) const; - management::Manageable::status_t ManagementMethod (uint32_t methodId, - management::Args& args, - std::string& text); + QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject() const; + QPID_BROKER_EXTERN management::Manageable* GetVhostObject() const; + QPID_BROKER_EXTERN management::Manageable::status_t ManagementMethod( + uint32_t methodId, management::Args& args, std::string& text); /** Add to the broker's protocolFactorys */ - void registerProtocolFactory(const std::string& name, boost::shared_ptr<sys::ProtocolFactory>); + QPID_BROKER_EXTERN void registerProtocolFactory( + const std::string& name, boost::shared_ptr<sys::ProtocolFactory>); /** Accept connections */ QPID_BROKER_EXTERN void accept(); @@ -273,15 +274,17 @@ public: /** Move messages from one queue to another. A zero quantity means to move all messages */ - uint32_t queueMoveMessages( const std::string& srcQueue, - const std::string& destQueue, - uint32_t qty, - const qpid::types::Variant::Map& filter); + QPID_BROKER_EXTERN uint32_t queueMoveMessages( + const std::string& srcQueue, + const std::string& destQueue, + uint32_t qty, + const qpid::types::Variant::Map& filter); - boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory(const std::string& name = TCP_TRANSPORT) const; + QPID_BROKER_EXTERN boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory( + const std::string& name = TCP_TRANSPORT) const; /** Expose poller so plugins can register their descriptors. */ - boost::shared_ptr<sys::Poller> getPoller(); + QPID_BROKER_EXTERN boost::shared_ptr<sys::Poller> getPoller(); boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; } void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; } @@ -291,7 +294,7 @@ public: /** Timer for tasks that must be synchronized if we are in a cluster */ sys::Timer& getClusterTimer() { return clusterTimer.get() ? *clusterTimer : timer; } - void setClusterTimer(std::auto_ptr<sys::Timer>); + QPID_BROKER_EXTERN void setClusterTimer(std::auto_ptr<sys::Timer>); boost::function<std::vector<Url> ()> getKnownBrokers; @@ -322,15 +325,14 @@ public: * context. *@return true if delivery of a message should be deferred. */ - boost::function<bool (const std::string& queue, - const boost::intrusive_ptr<Message>& msg)> deferDelivery; + boost::function<bool (const std::string& queue, const boost::intrusive_ptr<Message>& msg)> deferDelivery; bool isAuthenticating ( ) { return config.auth; } bool isTimestamping() { return config.timestampRcvMsgs; } typedef boost::function1<void, boost::shared_ptr<Queue> > QueueFunctor; - std::pair<boost::shared_ptr<Queue>, bool> createQueue( + QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Queue>, bool> createQueue( const std::string& name, bool durable, bool autodelete, @@ -339,30 +341,39 @@ public: const qpid::framing::FieldTable& arguments, const std::string& userId, const std::string& connectionId); - void deleteQueue(const std::string& name, - const std::string& userId, - const std::string& connectionId, - QueueFunctor check = QueueFunctor()); - std::pair<Exchange::shared_ptr, bool> createExchange( + + QPID_BROKER_EXTERN void deleteQueue( + const std::string& name, + const std::string& userId, + const std::string& connectionId, + QueueFunctor check = QueueFunctor()); + + QPID_BROKER_EXTERN std::pair<Exchange::shared_ptr, bool> createExchange( const std::string& name, const std::string& type, bool durable, const std::string& alternateExchange, const qpid::framing::FieldTable& args, const std::string& userId, const std::string& connectionId); - void deleteExchange(const std::string& name, const std::string& userId, - const std::string& connectionId); - void bind(const std::string& queue, - const std::string& exchange, - const std::string& key, - const qpid::framing::FieldTable& arguments, - const std::string& userId, - const std::string& connectionId); - void unbind(const std::string& queue, - const std::string& exchange, - const std::string& key, - const std::string& userId, - const std::string& connectionId); + + QPID_BROKER_EXTERN void deleteExchange( + const std::string& name, const std::string& userId, + const std::string& connectionId); + + QPID_BROKER_EXTERN void bind( + const std::string& queue, + const std::string& exchange, + const std::string& key, + const qpid::framing::FieldTable& arguments, + const std::string& userId, + const std::string& connectionId); + + QPID_BROKER_EXTERN void unbind( + const std::string& queue, + const std::string& exchange, + const std::string& key, + const std::string& userId, + const std::string& connectionId); ConsumerFactories& getConsumerFactories() { return consumerFactories; } ConnectionObservers& getConnectionObservers() { return connectionObservers; } diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp index 1e6aab217c..5e339cec03 100644 --- a/cpp/src/qpid/broker/Connection.cpp +++ b/cpp/src/qpid/broker/Connection.cpp @@ -185,11 +185,13 @@ void Connection::recordFromServer(const framing::AMQFrame& frame) // Don't record management stats in cluster-unsafe contexts if (mgmtObject != 0 && isClusterSafe()) { - mgmtObject->inc_framesToClient(); - mgmtObject->inc_bytesToClient(frame.encodedSize()); + qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics(); + cStats->framesToClient += 1; + cStats->bytesToClient += frame.encodedSize(); if (isMessage(frame.getMethod())) { - mgmtObject->inc_msgsToClient(); + cStats->msgsToClient += 1; } + mgmtObject->statisticsUpdated(); } } @@ -198,11 +200,13 @@ void Connection::recordFromClient(const framing::AMQFrame& frame) // Don't record management stats in cluster-unsafe contexts if (mgmtObject != 0 && isClusterSafe()) { - mgmtObject->inc_framesFromClient(); - mgmtObject->inc_bytesFromClient(frame.encodedSize()); + qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics(); + cStats->framesFromClient += 1; + cStats->bytesFromClient += frame.encodedSize(); if (isMessage(frame.getMethod())) { - mgmtObject->inc_msgsFromClient(); + cStats->msgsFromClient += 1; } + mgmtObject->statisticsUpdated(); } } diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h index 858ab6f7f4..1b8bd83139 100644 --- a/cpp/src/qpid/broker/Connection.h +++ b/cpp/src/qpid/broker/Connection.h @@ -113,15 +113,20 @@ class Connection : public sys::ConnectionInputHandler, void requestIOProcessing (boost::function0<void>); void recordFromServer (const framing::AMQFrame& frame); void recordFromClient (const framing::AMQFrame& frame); + + // gets for configured federation links std::string getAuthMechanism(); std::string getAuthCredentials(); std::string getUsername(); std::string getPassword(); std::string getHost(); uint16_t getPort(); + void notifyConnectionForced(const std::string& text); void setUserId(const std::string& uid); void raiseConnectEvent(); + + // credentials for connected client const std::string& getUserId() const { return ConnectionState::getUserId(); } const std::string& getMgmtId() const { return mgmtId; } management::ManagementAgent* getAgent() const { return agent; } diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp index f1d43c5cdb..6894324117 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.cpp +++ b/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -28,6 +28,7 @@ #include "qpid/framing/AllInvoker.h" #include "qpid/framing/ConnectionStartOkBody.h" #include "qpid/framing/enum.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include "qpid/sys/SecurityLayer.h" #include "qpid/broker/AclModule.h" diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h index 05c5f00c57..2e25543308 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.h +++ b/cpp/src/qpid/broker/ConnectionHandler.h @@ -35,7 +35,6 @@ #include "qpid/framing/ProtocolInitiation.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/Exception.h" -#include "qpid/broker/AclModule.h" #include "qpid/sys/SecurityLayer.h" diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp index 5591539853..5d9aea7509 100644 --- a/cpp/src/qpid/broker/DirectExchange.cpp +++ b/cpp/src/qpid/broker/DirectExchange.cpp @@ -153,8 +153,9 @@ bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, c return true; } -void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +void DirectExchange::route(Deliverable& msg) { + const string& routingKey = msg.getMessage().getRoutingKey(); PreRoute pr(msg, this); ConstBindingList b; { diff --git a/cpp/src/qpid/broker/DirectExchange.h b/cpp/src/qpid/broker/DirectExchange.h index a6f9cf91af..833be52c1c 100644 --- a/cpp/src/qpid/broker/DirectExchange.h +++ b/cpp/src/qpid/broker/DirectExchange.h @@ -57,9 +57,7 @@ public: const std::string& routingKey, const qpid::framing::FieldTable* args); virtual bool unbind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(boost::shared_ptr<Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args); diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp index febd547478..d482c2c327 100644 --- a/cpp/src/qpid/broker/DtxManager.cpp +++ b/cpp/src/qpid/broker/DtxManager.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/DtxManager.h" #include "qpid/broker/DtxTimeout.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/StructHelper.h" #include "qpid/log/Statement.h" #include "qpid/sys/Timer.h" #include "qpid/ptr_map.h" @@ -55,7 +56,7 @@ void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionCon bool DtxManager::prepare(const std::string& xid) { - QPID_LOG(debug, "preparing: " << xid); + QPID_LOG(debug, "preparing: " << convert(xid)); try { return getWork(xid)->prepare(); } catch (DtxTimeoutException& e) { @@ -66,7 +67,7 @@ bool DtxManager::prepare(const std::string& xid) bool DtxManager::commit(const std::string& xid, bool onePhase) { - QPID_LOG(debug, "committing: " << xid); + QPID_LOG(debug, "committing: " << convert(xid)); try { bool result = getWork(xid)->commit(onePhase); remove(xid); @@ -79,7 +80,7 @@ bool DtxManager::commit(const std::string& xid, bool onePhase) void DtxManager::rollback(const std::string& xid) { - QPID_LOG(debug, "rolling back: " << xid); + QPID_LOG(debug, "rolling back: " << convert(xid)); try { getWork(xid)->rollback(); remove(xid); @@ -94,7 +95,7 @@ DtxWorkRecord* DtxManager::getWork(const std::string& xid) Mutex::ScopedLock locker(lock); WorkMap::iterator i = work.find(xid); if (i == work.end()) { - throw NotFoundException(QPID_MSG("Unrecognised xid " << xid)); + throw NotFoundException(QPID_MSG("Unrecognised xid " << convert(xid))); } return ptr_map_ptr(i); } @@ -109,7 +110,7 @@ void DtxManager::remove(const std::string& xid) Mutex::ScopedLock locker(lock); WorkMap::iterator i = work.find(xid); if (i == work.end()) { - throw NotFoundException(QPID_MSG("Unrecognised xid " << xid)); + throw NotFoundException(QPID_MSG("Unrecognised xid " << convert(xid))); } else { work.erase(i); } @@ -120,7 +121,7 @@ DtxWorkRecord* DtxManager::createWork(const std::string& xid) Mutex::ScopedLock locker(lock); WorkMap::iterator i = work.find(xid); if (i != work.end()) { - throw NotAllowedException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)")); + throw NotAllowedException(QPID_MSG("Xid " << convert(xid) << " is already known (use 'join' to add work to an existing xid)")); } else { std::string ncxid = xid; // Work around const correctness problems in ptr_map. return ptr_map_ptr(work.insert(ncxid, new DtxWorkRecord(ncxid, store)).first); @@ -175,3 +176,19 @@ void DtxManager::setStore (TransactionalStore* _store) { store = _store; } + +std::string DtxManager::convert(const qpid::framing::Xid& xid) +{ + qpid::framing::StructHelper helper; + std::string encoded; + helper.encode(xid, encoded); + return encoded; +} + +qpid::framing::Xid DtxManager::convert(const std::string& xid) +{ + qpid::framing::StructHelper helper; + qpid::framing::Xid decoded; + helper.decode(decoded, xid); + return decoded; +} diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h index 11895695a3..6f03189f66 100644 --- a/cpp/src/qpid/broker/DtxManager.h +++ b/cpp/src/qpid/broker/DtxManager.h @@ -26,6 +26,7 @@ #include "qpid/broker/DtxWorkRecord.h" #include "qpid/broker/TransactionalStore.h" #include "qpid/framing/amqp_types.h" +#include "qpid/framing/Xid.h" #include "qpid/sys/Mutex.h" #include "qpid/ptr_map.h" @@ -74,6 +75,8 @@ public: } DtxWorkRecord* getWork(const std::string& xid); bool exists(const std::string& xid); + static std::string convert(const framing::Xid& xid); + static framing::Xid convert(const std::string& xid); }; } diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp index a413fe418d..2c26fec49f 100644 --- a/cpp/src/qpid/broker/DtxWorkRecord.cpp +++ b/cpp/src/qpid/broker/DtxWorkRecord.cpp @@ -19,6 +19,7 @@ * */ #include "qpid/broker/DtxWorkRecord.h" +#include "qpid/broker/DtxManager.h" #include "qpid/framing/reply_exceptions.h" #include <boost/format.hpp> #include <boost/mem_fn.hpp> @@ -73,7 +74,7 @@ bool DtxWorkRecord::commit(bool onePhase) if (prepared) { //already prepared i.e. 2pc if (onePhase) { - throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has been prepared, one-phase option not valid!")); + throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has been prepared, one-phase option not valid!")); } store->commit(*txn); @@ -84,7 +85,7 @@ bool DtxWorkRecord::commit(bool onePhase) } else { //1pc commit optimisation, don't need a 2pc transaction context: if (!onePhase) { - throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!")); + throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has not been prepared, one-phase option required!")); } std::auto_ptr<TransactionContext> localtxn = store->begin(); if (prepare(localtxn.get())) { @@ -116,10 +117,10 @@ void DtxWorkRecord::add(DtxBuffer::shared_ptr ops) { Mutex::ScopedLock locker(lock); if (expired) { - throw DtxTimeoutException(QPID_MSG("Branch with xid " << xid << " has timed out.")); + throw DtxTimeoutException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has timed out.")); } if (completed) { - throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!")); + throw CommandInvalidException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has been completed!")); } work.push_back(ops); } @@ -133,7 +134,7 @@ bool DtxWorkRecord::check() //iterate through all DtxBuffers and ensure they are all ended for (Work::iterator i = work.begin(); i != work.end(); i++) { if (!(*i)->isEnded()) { - throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " not completed!")); + throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " not completed!")); } else if ((*i)->isRollbackOnly()) { rolledback = true; } diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp index ecaa492903..8d20b0df81 100644 --- a/cpp/src/qpid/broker/Exchange.cpp +++ b/cpp/src/qpid/broker/Exchange.cpp @@ -32,7 +32,9 @@ #include "qpid/sys/ExceptionHolder.h" #include <stdexcept> -using namespace qpid::broker; +namespace qpid { +namespace broker { + using namespace qpid::framing; using qpid::framing::Buffer; using qpid::framing::FieldTable; @@ -135,20 +137,23 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); + qmf::org::apache::qpid::broker::Exchange::PerThreadStats *eStats = mgmtExchange->getStatistics(); + uint64_t contentSize = msg.contentSize(); + + eStats->msgReceives += 1; + eStats->byteReceives += contentSize; if (count == 0) { //QPID_LOG(warning, "Exchange " << getName() << " could not route message; no matching binding found"); - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); + eStats->msgDrops += 1; + eStats->byteDrops += contentSize; if (brokerMgmtObject) brokerMgmtObject->inc_discardsNoRoute(); } else { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + eStats->msgRoutes += count; + eStats->byteRoutes += count * contentSize; } } } @@ -156,7 +161,7 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b) void Exchange::routeIVE(){ if (ive && lastMsg.get()){ DeliverableMessage dmsg(lastMsg); - route(dmsg, lastMsg->getRoutingKey(), lastMsg->getApplicationHeaders()); + route(dmsg); } } @@ -399,9 +404,12 @@ void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) { bool Exchange::routeWithAlternate(Deliverable& msg) { - route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); + route(msg); if (!msg.delivered && alternate) { - alternate->route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); + alternate->route(msg); } return msg.delivered; } + +}} + diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h index 9179dd5c7c..7376f814ed 100644 --- a/cpp/src/qpid/broker/Exchange.h +++ b/cpp/src/qpid/broker/Exchange.h @@ -196,7 +196,7 @@ public: virtual bool unbind(boost::shared_ptr<Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; virtual bool isBound(boost::shared_ptr<Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args) = 0; QPID_BROKER_EXTERN virtual void setProperties(const boost::intrusive_ptr<Message>&); - virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual void route(Deliverable& msg) = 0; //PersistableExchange: QPID_BROKER_EXTERN void setPersistenceId(uint64_t id) const; diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp index fca77f7ddd..43d7268dfb 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.cpp +++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -24,6 +24,7 @@ #include "qpid/broker/FanOutExchange.h" #include "qpid/broker/HeadersExchange.h" #include "qpid/broker/TopicExchange.h" +#include "qpid/broker/Link.h" #include "qpid/management/ManagementDirectExchange.h" #include "qpid/management/ManagementTopicExchange.h" #include "qpid/framing/reply_exceptions.h" @@ -58,6 +59,8 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c exchange = Exchange::shared_ptr(new ManagementDirectExchange(name, durable, args, parent, broker)); }else if (type == ManagementTopicExchange::typeName) { exchange = Exchange::shared_ptr(new ManagementTopicExchange(name, durable, args, parent, broker)); + }else if (type == Link::exchangeTypeName) { + exchange = Link::linkExchangeFactory(name); }else{ FunctionMap::iterator i = factory.find(type); if (i == factory.end()) { diff --git a/cpp/src/qpid/broker/ExchangeRegistry.h b/cpp/src/qpid/broker/ExchangeRegistry.h index 90ef81b49e..27b705fbe5 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.h +++ b/cpp/src/qpid/broker/ExchangeRegistry.h @@ -54,7 +54,7 @@ class ExchangeRegistry{ bool durable, const qpid::framing::FieldTable& args = framing::FieldTable()); QPID_BROKER_EXTERN void destroy(const std::string& name); - Exchange::shared_ptr getDefault(); + QPID_BROKER_EXTERN Exchange::shared_ptr getDefault(); /** * Find the named exchange. Return 0 if not found. @@ -75,7 +75,7 @@ class ExchangeRegistry{ /** Register an exchange instance. *@return true if registered, false if exchange with same name is already registered. */ - bool registerExchange(const Exchange::shared_ptr&); + QPID_BROKER_EXTERN bool registerExchange(const Exchange::shared_ptr&); QPID_BROKER_EXTERN void registerType(const std::string& type, FactoryFunction); @@ -85,7 +85,7 @@ class ExchangeRegistry{ for (ExchangeMap::const_iterator i = exchanges.begin(); i != exchanges.end(); ++i) f(i->second); } - + private: typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap; typedef std::map<std::string, FactoryFunction > FunctionMap; diff --git a/cpp/src/qpid/broker/Fairshare.cpp b/cpp/src/qpid/broker/Fairshare.cpp index 313aa746f1..7cdad1a44f 100644 --- a/cpp/src/qpid/broker/Fairshare.cpp +++ b/cpp/src/qpid/broker/Fairshare.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/Fairshare.h" #include "qpid/broker/QueuedMessage.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include <boost/format.hpp> #include <boost/lexical_cast.hpp> diff --git a/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp index 5879fa0892..2bce99b6fe 100644 --- a/cpp/src/qpid/broker/FanOutExchange.cpp +++ b/cpp/src/qpid/broker/FanOutExchange.cpp @@ -101,7 +101,7 @@ bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, cons return true; } -void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/) +void FanOutExchange::route(Deliverable& msg) { PreRoute pr(msg, this); doRoute(msg, bindings.snapshot()); diff --git a/cpp/src/qpid/broker/FanOutExchange.h b/cpp/src/qpid/broker/FanOutExchange.h index 1a7d486796..c979fdca25 100644 --- a/cpp/src/qpid/broker/FanOutExchange.h +++ b/cpp/src/qpid/broker/FanOutExchange.h @@ -54,9 +54,7 @@ class FanOutExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp index 142c23f276..6648ae0422 100644 --- a/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/cpp/src/qpid/broker/HeadersExchange.cpp @@ -191,8 +191,9 @@ bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, } -void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args) +void HeadersExchange::route(Deliverable& msg) { + const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (!args) { //can't match if there were no headers passed in if (mgmtExchange != 0) { diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h index 3b939d6851..d10892b9cc 100644 --- a/cpp/src/qpid/broker/HeadersExchange.h +++ b/cpp/src/qpid/broker/HeadersExchange.h @@ -98,9 +98,7 @@ class HeadersExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, diff --git a/cpp/src/qpid/broker/LegacyLVQ.cpp b/cpp/src/qpid/broker/LegacyLVQ.cpp index 49c0a32c19..f1deddf4c8 100644 --- a/cpp/src/qpid/broker/LegacyLVQ.cpp +++ b/cpp/src/qpid/broker/LegacyLVQ.cpp @@ -28,16 +28,26 @@ namespace broker { LegacyLVQ::LegacyLVQ(const std::string& k, bool b, Broker* br) : MessageMap(k), noBrowse(b), broker(br) {} void LegacyLVQ::setNoBrowse(bool b) -{ +{ noBrowse = b; } +bool LegacyLVQ::deleted(const QueuedMessage& message) +{ + Ordering::iterator i = messages.find(message.position); + if (i != messages.end() && i->second.payload == message.payload) { + erase(i); + return true; + } else { + return false; + } +} bool LegacyLVQ::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { Ordering::iterator i = messages.find(position); - if (i != messages.end() && i->second.payload == message.payload) { + if (i != messages.end() && i->second.payload == message.payload && i->second.status == QueuedMessage::AVAILABLE) { + i->second.status = QueuedMessage::ACQUIRED; message = i->second; - erase(i); return true; } else { return false; @@ -66,12 +76,17 @@ bool LegacyLVQ::push(const QueuedMessage& added, QueuedMessage& removed) } const QueuedMessage& LegacyLVQ::replace(const QueuedMessage& original, const QueuedMessage& update) -{ +{ //add the new message into the original position of the replaced message Ordering::iterator i = messages.find(original.position); - i->second = update; - i->second.position = original.position; - return i->second; + if (i != messages.end()) { + i->second = update; + i->second.position = original.position; + return i->second; + } else { + QPID_LOG(error, "Failed to replace message at " << original.position); + return update; + } } void LegacyLVQ::removeIf(Predicate p) diff --git a/cpp/src/qpid/broker/LegacyLVQ.h b/cpp/src/qpid/broker/LegacyLVQ.h index 695e51131d..9355069f37 100644 --- a/cpp/src/qpid/broker/LegacyLVQ.h +++ b/cpp/src/qpid/broker/LegacyLVQ.h @@ -40,6 +40,7 @@ class LegacyLVQ : public MessageMap { public: LegacyLVQ(const std::string& key, bool noBrowse = false, Broker* broker = 0); + bool deleted(const QueuedMessage&); bool acquire(const framing::SequenceNumber&, QueuedMessage&); bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); bool push(const QueuedMessage& added, QueuedMessage& removed); diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp index 4af1e6d6bd..f21c861149 100644 --- a/cpp/src/qpid/broker/Link.cpp +++ b/cpp/src/qpid/broker/Link.cpp @@ -31,6 +31,8 @@ #include "qpid/framing/enum.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/broker/AclModule.h" +#include "qpid/broker/Exchange.h" +#include "qpid/UrlArray.h" namespace qpid { namespace broker { @@ -48,6 +50,13 @@ using std::stringstream; using std::string; namespace _qmf = ::qmf::org::apache::qpid::broker; + +namespace { + const std::string FAILOVER_EXCHANGE("amq.failover"); + const std::string FAILOVER_HEADER_KEY("amq.failover"); +} + + struct LinkTimerTask : public sys::TimerTask { LinkTimerTask(Link& l, sys::Timer& t) : TimerTask(int64_t(l.getBroker()->getOptions().linkMaintenanceInterval* @@ -65,6 +74,57 @@ struct LinkTimerTask : public sys::TimerTask { sys::Timer& timer; }; + + +/** LinkExchange is used by the link to subscribe to the remote broker's amq.failover exchange. + */ +class LinkExchange : public broker::Exchange +{ +public: + LinkExchange(const std::string& name) : Exchange(name), link(0) {} + ~LinkExchange() {}; + std::string getType() const { return Link::exchangeTypeName; } + + // Exchange methods - set up to prevent binding/unbinding etc from clients! + bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*) { return false; } + bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*) { return false; } + bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const) {return false;} + + // Process messages sent from the remote's amq.failover exchange by extracting the failover URLs + // and saving them should the Link need to reconnect. + void route(broker::Deliverable& msg) + { + if (!link) return; + const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders(); + framing::Array addresses; + if (headers && headers->getArray(FAILOVER_HEADER_KEY, addresses)) { + // convert the Array of addresses to a single Url container for used with setUrl(): + std::vector<Url> urlVec; + Url urls; + urlVec = urlArrayToVector(addresses); + for(size_t i = 0; i < urlVec.size(); ++i) + urls.insert(urls.end(), urlVec[i].begin(), urlVec[i].end()); + QPID_LOG(debug, "Remote broker has provided these failover addresses= " << urls); + link->setUrl(urls); + } + } + + void setLink(Link *_link) + { + assert(!link); + link = _link; + } + +private: + Link *link; +}; + + +boost::shared_ptr<Exchange> Link::linkExchangeFactory( const std::string& _name ) +{ + return Exchange::shared_ptr(new LinkExchange(_name)); +} + Link::Link(LinkRegistry* _links, MessageStore* _store, const string& _host, @@ -76,8 +136,9 @@ Link::Link(LinkRegistry* _links, const string& _password, Broker* _broker, Manageable* parent) - : links(_links), store(_store), host(_host), port(_port), - transport(_transport), + : links(_links), store(_store), + configuredTransport(_transport), configuredHost(_host), configuredPort(_port), + host(_host), port(_port), transport(_transport), durable(_durable), authMechanism(_authMechanism), username(_username), password(_password), persistenceId(0), mgmtObject(0), broker(_broker), state(0), @@ -88,7 +149,8 @@ Link::Link(LinkRegistry* _links, channelCounter(1), connection(0), agent(0), - timerTask(new LinkTimerTask(*this, broker->getTimer())) + timerTask(new LinkTimerTask(*this, broker->getTimer())), + failoverChannel(0) { if (parent != 0 && broker != 0) { @@ -106,15 +168,26 @@ Link::Link(LinkRegistry* _links, startConnectionLH(); } broker->getTimer().add(timerTask); + + stringstream _name; + _name << "qpid.link." << transport << ":" << host << ":" << port; + std::pair<Exchange::shared_ptr, bool> rc = broker->getExchanges().declare(_name.str(), + exchangeTypeName); + failoverExchange = boost::static_pointer_cast<LinkExchange>(rc.first); + assert(failoverExchange); + failoverExchange->setLink(this); } Link::~Link () { - if (state == STATE_OPERATIONAL && connection != 0) - connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); + if (state == STATE_OPERATIONAL && connection != 0) { + closeConnection("closed by management"); + } if (mgmtObject != 0) mgmtObject->resourceDestroy (); + + broker->getExchanges().destroy(failoverExchange->getName()); } void Link::setStateLH (int newState) @@ -180,11 +253,21 @@ void Link::established(Connection* c) void Link::setUrl(const Url& u) { + QPID_LOG(info, "Setting remote broker failover addresses for link '" << getName() << "' to these urls: " << u); Mutex::ScopedLock mutex(lock); url = u; reconnectNext = 0; } + +namespace { + /** invoked when session used to subscribe to remote's amq.failover exchange detaches */ + void sessionDetached(Link *link) { + QPID_LOG(debug, "detached from 'amq.failover' for link: " << link->getName()); + } +} + + void Link::opened() { Mutex::ScopedLock mutex(lock); if (!connection) return; @@ -198,37 +281,74 @@ void Link::opened() { reconnectNext = 0; QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << url); } + + // + // attempt to subscribe to failover exchange for updates from remote + // + + const std::string queueName = "qpid.link." + framing::Uuid(true).str(); + failoverChannel = nextChannel(); + + SessionHandler& sessionHandler = connection->getChannel(failoverChannel); + sessionHandler.setDetachedCallback( boost::bind(&sessionDetached, this) ); + failoverSession = queueName; + sessionHandler.attachAs(failoverSession); + + framing::AMQP_ServerProxy remoteBroker(sessionHandler.out); + + remoteBroker.getQueue().declare(queueName, + "", // alt-exchange + false, // passive + false, // durable + true, // exclusive + true, // auto-delete + FieldTable()); + remoteBroker.getExchange().bind(queueName, + FAILOVER_EXCHANGE, + "", // no key + FieldTable()); + remoteBroker.getMessage().subscribe(queueName, + failoverExchange->getName(), + 1, // implied-accept mode + 0, // pre-acquire mode + false, // exclusive + "", // resume-id + 0, // resume-ttl + FieldTable()); + remoteBroker.getMessage().flow(failoverExchange->getName(), 0, 0xFFFFFFFF); + remoteBroker.getMessage().flow(failoverExchange->getName(), 1, 0xFFFFFFFF); } void Link::closed(int, std::string text) { - Mutex::ScopedLock mutex(lock); - QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text); - - connection = 0; + bool isClosing = false; + { + Mutex::ScopedLock mutex(lock); + QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text); - if (state == STATE_OPERATIONAL) { - stringstream addr; - addr << host << ":" << port; - QPID_LOG(warning, "Inter-broker link disconnected from " << addr.str()); - if (!hideManagement() && agent) - agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); - } + connection = 0; + if (state == STATE_OPERATIONAL) { + stringstream addr; + addr << host << ":" << port; + if (!hideManagement() && agent) + agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); + } - for (Bridges::iterator i = active.begin(); i != active.end(); i++) { - (*i)->closed(); - created.push_back(*i); - } - active.clear(); + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); + created.push_back(*i); + } + active.clear(); - if (state != STATE_FAILED && state != STATE_PASSIVE) - { - setStateLH(STATE_WAITING); - if (!hideManagement()) - mgmtObject->set_lastError (text); + if (state != STATE_FAILED && state != STATE_PASSIVE) + { + setStateLH(STATE_WAITING); + if (!hideManagement()) + mgmtObject->set_lastError (text); + } } - - if (closing) + // Call destroy outside of the lock, don't want to be deleted with lock held. + if (isClosing) destroy(); } @@ -239,10 +359,8 @@ void Link::destroy () { Mutex::ScopedLock mutex(lock); - QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management"); - if (connection) - connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); - connection = 0; + QPID_LOG (info, "Inter-broker link to " << configuredHost << ":" << configuredPort << " removed by management"); + closeConnection("closed by management"); setStateLH(STATE_CLOSED); // Move the bridges to be deleted into a local vector so there is no @@ -263,7 +381,7 @@ void Link::destroy () for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++) (*i)->destroy(); toDelete.clear(); - links->destroy (host, port); + links->destroy (configuredHost, configuredPort); } void Link::add(Bridge::shared_ptr bridge) @@ -311,7 +429,7 @@ void Link::ioThreadProcessing() // check for bridge session errors and recover if (!active.empty()) { Bridges::iterator removed = std::remove_if( - active.begin(), active.end(), !boost::bind(&Bridge::isSessionReady, _1)); + active.begin(), active.end(), boost::bind(&Bridge::isDetached, _1)); for (Bridges::iterator i = removed; i != active.end(); ++i) { Bridge::shared_ptr bridge = *i; bridge->closed(); @@ -398,14 +516,14 @@ bool Link::hideManagement() const { uint Link::nextChannel() { Mutex::ScopedLock mutex(lock); - + if (channelCounter >= framing::CHANNEL_MAX) + channelCounter = 1; return channelCounter++; } void Link::notifyConnectionForced(const string text) { Mutex::ScopedLock mutex(lock); - setStateLH(STATE_FAILED); if (!hideManagement()) mgmtObject->set_lastError(text); @@ -418,7 +536,7 @@ void Link::setPersistenceId(uint64_t id) const const string& Link::getName() const { - return host; + return configuredHost; } Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer) @@ -444,9 +562,9 @@ Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer) void Link::encode(Buffer& buffer) const { buffer.putShortString(string("link")); - buffer.putShortString(host); - buffer.putShort(port); - buffer.putShortString(transport); + buffer.putShortString(configuredHost); + buffer.putShort(configuredPort); + buffer.putShortString(configuredTransport); buffer.putOctet(durable ? 1 : 0); buffer.putShortString(authMechanism); buffer.putShortString(username); @@ -455,10 +573,10 @@ void Link::encode(Buffer& buffer) const uint32_t Link::encodedSize() const { - return host.size() + 1 // short-string (host) + return configuredHost.size() + 1 // short-string (host) + 5 // short-string ("link") + 2 // port - + transport.size() + 1 // short-string(transport) + + configuredTransport.size() + 1 // short-string(transport) + 1 // durable + authMechanism.size() + 1 + username.size() + 1 @@ -513,7 +631,7 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& te } std::pair<Bridge::shared_ptr, bool> result = - links->declare (host, port, iargs.i_durable, iargs.i_src, + links->declare (configuredHost, configuredPort, iargs.i_durable, iargs.i_src, iargs.i_dest, iargs.i_key, iargs.i_srcIsQueue, iargs.i_srcIsLocal, iargs.i_tag, iargs.i_excludes, iargs.i_dynamic, iargs.i_sync); @@ -542,4 +660,63 @@ void Link::setPassive(bool passive) } } + +/** utility to clean up connection resources correctly */ +void Link::closeConnection( const std::string& reason) +{ + if (connection != 0) { + // cancel our subscription to the failover exchange + SessionHandler& sessionHandler = connection->getChannel(failoverChannel); + if (sessionHandler.getSession()) { + framing::AMQP_ServerProxy remoteBroker(sessionHandler.out); + remoteBroker.getMessage().cancel(failoverExchange->getName()); + remoteBroker.getSession().detach(failoverSession); + } + connection->close(CLOSE_CODE_CONNECTION_FORCED, reason); + connection = 0; + } +} + +/** returns the current remote's address, and connection state */ +bool Link::getRemoteAddress(qpid::Address& addr) const +{ + addr.protocol = transport; + addr.host = host; + addr.port = port; + + return state == STATE_OPERATIONAL; +} + + +// FieldTable keys for internal state data +namespace { + const std::string FAILOVER_ADDRESSES("failover-addresses"); + const std::string FAILOVER_INDEX("failover-index"); +} + +void Link::getState(framing::FieldTable& state) const +{ + state.clear(); + Mutex::ScopedLock mutex(lock); + if (!url.empty()) { + state.setString(FAILOVER_ADDRESSES, url.str()); + state.setInt(FAILOVER_INDEX, reconnectNext); + } +} + +void Link::setState(const framing::FieldTable& state) +{ + Mutex::ScopedLock mutex(lock); + if (state.isSet(FAILOVER_ADDRESSES)) { + Url failovers(state.getAsString(FAILOVER_ADDRESSES)); + setUrl(failovers); + } + if (state.isSet(FAILOVER_INDEX)) { + reconnectNext = state.getAsInt(FAILOVER_INDEX); + } +} + + +const std::string Link::exchangeTypeName("qpid.LinkExchange"); + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h index 4085c3bfcf..a97fa48664 100644 --- a/cpp/src/qpid/broker/Link.h +++ b/cpp/src/qpid/broker/Link.h @@ -24,9 +24,11 @@ #include <boost/shared_ptr.hpp> #include "qpid/Url.h" +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/MessageStore.h" #include "qpid/broker/PersistableConfig.h" #include "qpid/broker/Bridge.h" +#include "qpid/broker/BrokerImportExport.h" #include "qpid/sys/Mutex.h" #include "qpid/framing/FieldTable.h" #include "qpid/management/Manageable.h" @@ -45,15 +47,23 @@ namespace broker { class LinkRegistry; class Broker; class Connection; +class LinkExchange; class Link : public PersistableConfig, public management::Manageable { private: - sys::Mutex lock; + mutable sys::Mutex lock; LinkRegistry* links; MessageStore* store; - std::string host; - uint16_t port; - std::string transport; + + // these remain constant across failover - used to identify this link + const std::string configuredTransport; + const std::string configuredHost; + const uint16_t configuredPort; + // these reflect the current address of remote - will change during failover + std::string host; + uint16_t port; + std::string transport; + bool durable; std::string authMechanism; std::string username; @@ -75,8 +85,10 @@ class Link : public PersistableConfig, public management::Manageable { uint channelCounter; Connection* connection; management::ManagementAgent* agent; - boost::intrusive_ptr<sys::TimerTask> timerTask; + boost::shared_ptr<broker::LinkExchange> failoverExchange; // subscribed to remote's amq.failover exchange + uint failoverChannel; + std::string failoverSession; static const int STATE_WAITING = 1; static const int STATE_CONNECTING = 2; @@ -94,6 +106,14 @@ class Link : public PersistableConfig, public management::Manageable { bool tryFailoverLH(); // Called during maintenance visit bool hideManagement() const; + void established(Connection*); // Called when connection is create + void opened(); // Called when connection is open (after create) + void closed(int, std::string); // Called when connection goes away + void reconnectLH(const Address&); //called by LinkRegistry + void closeConnection(const std::string& reason); + + friend class LinkRegistry; // to call established, opened, closed + public: typedef boost::shared_ptr<Link> shared_ptr; @@ -110,22 +130,25 @@ class Link : public PersistableConfig, public management::Manageable { management::Manageable* parent = 0); virtual ~Link(); - std::string getHost() { return host; } - uint16_t getPort() { return port; } - std::string getTransport() { return transport; } + /** these return the *configured* transport/host/port, which does not change over the + lifetime of the Link */ + std::string getHost() const { return configuredHost; } + uint16_t getPort() const { return configuredPort; } + std::string getTransport() const { return configuredTransport; } + + /** returns the current address of the remote, which may be different from the + configured transport/host/port due to failover. Returns true if connection is + active */ + bool getRemoteAddress(qpid::Address& addr) const; bool isDurable() { return durable; } void maintenanceVisit (); uint nextChannel(); void add(Bridge::shared_ptr); void cancel(Bridge::shared_ptr); - void setUrl(const Url&); // Set URL for reconnection. - void established(Connection*); // Called when connection is create - void opened(); // Called when connection is open (after create) - void closed(int, std::string); // Called when connection goes away - void reconnectLH(const Address&); //called by LinkRegistry - void close(); // Close the link from within the broker. + QPID_BROKER_EXTERN void setUrl(const Url&); // Set URL for reconnection. + QPID_BROKER_EXTERN void close(); // Close the link from within the broker. std::string getAuthMechanism() { return authMechanism; } std::string getUsername() { return username; } @@ -148,6 +171,13 @@ class Link : public PersistableConfig, public management::Manageable { management::ManagementObject* GetManagementObject(void) const; management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&); + // manage the exchange owned by this link + static const std::string exchangeTypeName; + static boost::shared_ptr<Exchange> linkExchangeFactory(const std::string& name); + + // replicate internal state of this Link for clustering + void getState(framing::FieldTable& state) const; + void setState(const framing::FieldTable& state); }; } } diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp index bb602bb953..d89f220d1b 100644 --- a/cpp/src/qpid/broker/LinkRegistry.cpp +++ b/cpp/src/qpid/broker/LinkRegistry.cpp @@ -25,7 +25,9 @@ #include <iostream> #include <boost/format.hpp> -using namespace qpid::broker; +namespace qpid { +namespace broker { + using namespace qpid::sys; using std::string; using std::pair; @@ -45,16 +47,15 @@ LinkRegistry::LinkRegistry () : { } -namespace { -struct ConnectionObserverImpl : public ConnectionObserver { +class LinkRegistryConnectionObserver : public ConnectionObserver { LinkRegistry& links; - ConnectionObserverImpl(LinkRegistry& l) : links(l) {} + public: + LinkRegistryConnectionObserver(LinkRegistry& l) : links(l) {} void connection(Connection& c) { links.notifyConnection(c.getMgmtId(), &c); } void opened(Connection& c) { links.notifyOpened(c.getMgmtId()); } void closed(Connection& c) { links.notifyClosed(c.getMgmtId()); } void forced(Connection& c, const string& text) { links.notifyConnectionForced(c.getMgmtId(), text); } }; -} LinkRegistry::LinkRegistry (Broker* _broker) : broker(_broker), @@ -62,7 +63,7 @@ LinkRegistry::LinkRegistry (Broker* _broker) : realm(broker->getOptions().realm) { broker->getConnectionObservers().add( - boost::shared_ptr<ConnectionObserver>(new ConnectionObserverImpl(*this))); + boost::shared_ptr<ConnectionObserver>(new LinkRegistryConnectionObserver(*this))); } LinkRegistry::~LinkRegistry() {} @@ -298,22 +299,29 @@ std::string LinkRegistry::getUsername(const std::string& key) return link->getUsername(); } +/** note: returns the current remote host (may be different from the host originally + configured for the Link due to failover) */ std::string LinkRegistry::getHost(const std::string& key) { - Link::shared_ptr link = findLink(key); - if (!link) - return string(); + Link::shared_ptr link = findLink(key); + if (!link) + return string(); - return link->getHost(); + qpid::Address addr; + link->getRemoteAddress(addr); + return addr.host; } +/** returns the current remote port (ditto above) */ uint16_t LinkRegistry::getPort(const std::string& key) { Link::shared_ptr link = findLink(key); if (!link) return 0; - return link->getPort(); + qpid::Address addr; + link->getRemoteAddress(addr); + return addr.port; } std::string LinkRegistry::getPassword(const std::string& key) @@ -368,3 +376,4 @@ void LinkRegistry::eachBridge(boost::function<void(boost::shared_ptr<Bridge>)> f for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) f(i->second); } +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h index 753f6bfe9e..8e9d2f4b0d 100644 --- a/cpp/src/qpid/broker/LinkRegistry.h +++ b/cpp/src/qpid/broker/LinkRegistry.h @@ -23,6 +23,7 @@ */ #include <map> +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/Bridge.h" #include "qpid/broker/MessageStore.h" #include "qpid/Address.h" @@ -56,43 +57,50 @@ namespace broker { static std::string createKey(const Address& address); static std::string createKey(const std::string& host, uint16_t port); + // Methods called by the connection observer. + void notifyConnection (const std::string& key, Connection* c); + void notifyOpened (const std::string& key); + void notifyClosed (const std::string& key); + void notifyConnectionForced (const std::string& key, const std::string& text); + friend class LinkRegistryConnectionObserver; + public: - LinkRegistry (); // Only used in store tests - LinkRegistry (Broker* _broker); - ~LinkRegistry(); - - std::pair<boost::shared_ptr<Link>, bool> - declare(const std::string& host, - uint16_t port, - const std::string& transport, - bool durable, - const std::string& authMechanism, - const std::string& username, - const std::string& password); - - std::pair<Bridge::shared_ptr, bool> - declare(const std::string& host, - uint16_t port, - bool durable, - const std::string& src, - const std::string& dest, - const std::string& key, - bool isQueue, - bool isLocal, - const std::string& id, - const std::string& excludes, - bool dynamic, - uint16_t sync, - Bridge::InitializeCallback=0 - ); - - void destroy(const std::string& host, const uint16_t port); - - void destroy(const std::string& host, - const uint16_t port, - const std::string& src, - const std::string& dest, - const std::string& key); + QPID_BROKER_EXTERN LinkRegistry (); // Only used in store tests + QPID_BROKER_EXTERN LinkRegistry (Broker* _broker); + QPID_BROKER_EXTERN ~LinkRegistry(); + + QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Link>, bool> + declare(const std::string& host, + uint16_t port, + const std::string& transport, + bool durable, + const std::string& authMechanism, + const std::string& username, + const std::string& password); + + QPID_BROKER_EXTERN std::pair<Bridge::shared_ptr, bool> + declare(const std::string& host, + uint16_t port, + bool durable, + const std::string& src, + const std::string& dest, + const std::string& key, + bool isQueue, + bool isLocal, + const std::string& id, + const std::string& excludes, + bool dynamic, + uint16_t sync, + Bridge::InitializeCallback=0 + ); + + QPID_BROKER_EXTERN void destroy(const std::string& host, const uint16_t port); + + QPID_BROKER_EXTERN void destroy(const std::string& host, + const uint16_t port, + const std::string& src, + const std::string& dest, + const std::string& key); /** * Register the manageable parent for declared queues @@ -102,24 +110,20 @@ namespace broker { /** * Set the store to use. May only be called once. */ - void setStore (MessageStore*); + QPID_BROKER_EXTERN void setStore (MessageStore*); /** * Return the message store used. */ - MessageStore* getStore() const; + QPID_BROKER_EXTERN MessageStore* getStore() const; - void notifyConnection (const std::string& key, Connection* c); - void notifyOpened (const std::string& key); - void notifyClosed (const std::string& key); - void notifyConnectionForced (const std::string& key, const std::string& text); - std::string getAuthMechanism (const std::string& key); - std::string getAuthCredentials (const std::string& key); - std::string getAuthIdentity (const std::string& key); - std::string getUsername (const std::string& key); - std::string getPassword (const std::string& key); - std::string getHost (const std::string& key); - uint16_t getPort (const std::string& key); + QPID_BROKER_EXTERN std::string getAuthMechanism (const std::string& key); + QPID_BROKER_EXTERN std::string getAuthCredentials (const std::string& key); + QPID_BROKER_EXTERN std::string getAuthIdentity (const std::string& key); + QPID_BROKER_EXTERN std::string getUsername (const std::string& key); + QPID_BROKER_EXTERN std::string getPassword (const std::string& key); + QPID_BROKER_EXTERN std::string getHost (const std::string& key); + QPID_BROKER_EXTERN uint16_t getPort (const std::string& key); /** * Called by links failing over to new address @@ -132,13 +136,13 @@ namespace broker { * updated but links won't actually establish connections and * bridges won't therefore pull or push any messages. */ - void setPassive(bool); - bool isPassive() { return passive; } + QPID_BROKER_EXTERN void setPassive(bool); + QPID_BROKER_EXTERN bool isPassive() { return passive; } /** Iterate over each link in the registry. Used for cluster updates. */ - void eachLink(boost::function<void(boost::shared_ptr<Link>)> f); + QPID_BROKER_EXTERN void eachLink(boost::function<void(boost::shared_ptr<Link>)> f); /** Iterate over each bridge in the registry. Used for cluster updates. */ - void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f); + QPID_BROKER_EXTERN void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f); }; } } diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp index ae4503328a..40dfba39f4 100644 --- a/cpp/src/qpid/broker/Message.cpp +++ b/cpp/src/qpid/broker/Message.cpp @@ -131,6 +131,7 @@ uint32_t Message::getRequiredCredit() void Message::encode(framing::Buffer& buffer) const { + sys::Mutex::ScopedLock l(lock); //encode method and header frames EncodeFrame f1(buffer); frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>()); @@ -142,6 +143,7 @@ void Message::encode(framing::Buffer& buffer) const void Message::encodeContent(framing::Buffer& buffer) const { + sys::Mutex::ScopedLock l(lock); //encode the payload of each content frame EncodeBody f2(buffer); frames.map_if(f2, TypeFilter<CONTENT_BODY>()); @@ -154,11 +156,13 @@ uint32_t Message::encodedSize() const uint32_t Message::encodedContentSize() const { + sys::Mutex::ScopedLock l(lock); return frames.getContentSize(); } uint32_t Message::encodedHeaderSize() const { + sys::Mutex::ScopedLock l(lock); // prevent modifications while computing size //add up the size for all method and header frames in the frameset SumFrameSize sum; frames.map_if(sum, TypeFilter2<METHOD_BODY, HEADER_BODY>()); @@ -218,8 +222,9 @@ void Message::releaseContent() store->stage(pmsg); staged = true; } - //ensure required credit is cached before content frames are released + //ensure required credit and size is cached before content frames are released getRequiredCredit(); + contentSize(); //remove any content frames from the frameset frames.remove(TypeFilter<CONTENT_BODY>()); setContentReleased(); @@ -354,6 +359,7 @@ public: AMQHeaderBody* Message::getHeaderBody() { + // expects lock to be held if (copyHeaderOnWrite) { CloneHeaderBody f; frames.map_if(f, TypeFilter<HEADER_BODY>()); diff --git a/cpp/src/qpid/broker/MessageDeque.cpp b/cpp/src/qpid/broker/MessageDeque.cpp index 709d99876b..f70c996975 100644 --- a/cpp/src/qpid/broker/MessageDeque.cpp +++ b/cpp/src/qpid/broker/MessageDeque.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/MessageDeque.h" #include "qpid/broker/QueuedMessage.h" #include "qpid/log/Statement.h" +#include "assert.h" namespace qpid { namespace broker { @@ -39,7 +40,7 @@ size_t MessageDeque::index(const framing::SequenceNumber& position) bool MessageDeque::deleted(const QueuedMessage& m) { size_t i = index(m.position); - if (i < messages.size()) { + if (i < messages.size() && messages[i].status != QueuedMessage::DELETED) { messages[i].status = QueuedMessage::DELETED; clean(); return true; @@ -53,7 +54,7 @@ size_t MessageDeque::size() return available; } -void MessageDeque::release(const QueuedMessage& message) +QueuedMessage* MessageDeque::releasePtr(const QueuedMessage& message) { size_t i = index(message.position); if (i < messages.size()) { @@ -62,12 +63,17 @@ void MessageDeque::release(const QueuedMessage& message) if (head > i) head = i; m.status = QueuedMessage::AVAILABLE; ++available; + return &messages[i]; } } else { + assert(0); QPID_LOG(error, "Failed to release message at " << message.position << " " << message.payload->getFrames().getContent() << "; no such message (index=" << i << ", size=" << messages.size() << ")"); } + return 0; } +void MessageDeque::release(const QueuedMessage& message) { releasePtr(message); } + bool MessageDeque::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { if (position < messages.front().position) return false; @@ -129,8 +135,7 @@ QueuedMessage padding(uint32_t pos) { } } // namespace -bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) -{ +QueuedMessage* MessageDeque::pushPtr(const QueuedMessage& added) { //add padding to prevent gaps in sequence, which break the index //calculation (needed for queue replication) while (messages.size() && (added.position - messages.back().position) > 1) @@ -139,7 +144,12 @@ bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed* messages.back().status = QueuedMessage::AVAILABLE; if (head >= messages.size()) head = messages.size() - 1; ++available; - return false;//adding a message never causes one to be removed for deque + return &messages.back(); +} + +bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) { + pushPtr(added); + return false; // adding a message never causes one to be removed for deque } void MessageDeque::updateAcquired(const QueuedMessage& acquired) diff --git a/cpp/src/qpid/broker/MessageDeque.h b/cpp/src/qpid/broker/MessageDeque.h index bb5943b09b..9b53716d4e 100644 --- a/cpp/src/qpid/broker/MessageDeque.h +++ b/cpp/src/qpid/broker/MessageDeque.h @@ -48,6 +48,12 @@ class MessageDeque : public Messages void foreach(Functor); void removeIf(Predicate); + // For use by other Messages implementations that use MessageDeque as a FIFO index + // and keep pointers to its elements in their own indexing strctures. + void clean(); + QueuedMessage* releasePtr(const QueuedMessage&); + QueuedMessage* pushPtr(const QueuedMessage& added); + private: typedef std::deque<QueuedMessage> Deque; Deque messages; @@ -55,7 +61,6 @@ class MessageDeque : public Messages size_t head; size_t index(const framing::SequenceNumber&); - void clean(); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/MessageGroupManager.cpp b/cpp/src/qpid/broker/MessageGroupManager.cpp index 5f450cd556..15cd56a676 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.cpp +++ b/cpp/src/qpid/broker/MessageGroupManager.cpp @@ -19,11 +19,13 @@ * */ +#include "qpid/broker/MessageGroupManager.h" + +#include "qpid/broker/Queue.h" #include "qpid/framing/FieldTable.h" -#include "qpid/types/Variant.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" -#include "qpid/broker/Queue.h" -#include "qpid/broker/MessageGroupManager.h" +#include "qpid/types/Variant.h" using namespace qpid::broker; @@ -43,9 +45,18 @@ const std::string MessageGroupManager::qpidSharedGroup("qpid.shared_msg_group"); const std::string MessageGroupManager::qpidMessageGroupTimestamp("qpid.group_timestamp"); +/** return an iterator to the message at position, or members.end() if not found */ +MessageGroupManager::GroupState::MessageFifo::iterator +MessageGroupManager::GroupState::findMsg(const qpid::framing::SequenceNumber &position) +{ + MessageState mState(position); + MessageFifo::iterator found = std::lower_bound(members.begin(), members.end(), mState); + return (found->position == position) ? found : members.end(); +} + void MessageGroupManager::unFree( const GroupState& state ) { - GroupFifo::iterator pos = freeGroups.find( state.members.front() ); + GroupFifo::iterator pos = freeGroups.find( state.members.front().position ); assert( pos != freeGroups.end() && pos->second == &state ); freeGroups.erase( pos ); } @@ -60,8 +71,8 @@ void MessageGroupManager::disown( GroupState& state ) { state.owner.clear(); assert(state.members.size()); - assert(freeGroups.find(state.members.front()) == freeGroups.end()); - freeGroups[state.members.front()] = &state; + assert(freeGroups.find(state.members.front().position) == freeGroups.end()); + freeGroups[state.members.front().position] = &state; } MessageGroupManager::GroupState& MessageGroupManager::findGroup( const QueuedMessage& qm ) @@ -106,7 +117,8 @@ void MessageGroupManager::enqueued( const QueuedMessage& qm ) // @todo KAG optimization - store reference to group state in QueuedMessage // issue: const-ness?? GroupState& state = findGroup(qm); - state.members.push_back(qm.position); + GroupState::MessageState mState(qm.position); + state.members.push_back(mState); uint32_t total = state.members.size(); QPID_LOG( trace, "group queue " << qName << ": added message to group id=" << state.group << " total=" << total ); @@ -123,7 +135,9 @@ void MessageGroupManager::acquired( const QueuedMessage& qm ) // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? GroupState& state = findGroup(qm); - assert(state.members.size()); // there are msgs present + GroupState::MessageFifo::iterator m = state.findMsg(qm.position); + assert(m != state.members.end()); + m->acquired = true; state.acquired += 1; QPID_LOG( trace, "group queue " << qName << ": acquired message in group id=" << state.group << " acquired=" << state.acquired ); @@ -137,6 +151,9 @@ void MessageGroupManager::requeued( const QueuedMessage& qm ) GroupState& state = findGroup(qm); assert( state.acquired != 0 ); state.acquired -= 1; + GroupState::MessageFifo::iterator m = state.findMsg(qm.position); + assert(m != state.members.end()); + m->acquired = false; if (state.acquired == 0 && state.owned()) { QPID_LOG( trace, "group queue " << qName << ": consumer name=" << state.owner << " released group id=" << state.group); @@ -152,13 +169,17 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage // issue: const-ness?? GroupState& state = findGroup(qm); - assert( state.members.size() != 0 ); - assert( state.acquired != 0 ); - state.acquired -= 1; + GroupState::MessageFifo::iterator m = state.findMsg(qm.position); + assert(m != state.members.end()); + if (m->acquired) { + assert( state.acquired != 0 ); + state.acquired -= 1; + } - // likely to be at or near begin() if dequeued in order + // special case if qm is first (oldest) message in the group: + // may need to re-insert it back on the freeGroups list, as the index will change bool reFreeNeeded = false; - if (state.members.front() == qm.position) { + if (m == state.members.begin()) { if (!state.owned()) { // will be on the freeGroups list if mgmt is dequeueing rather than a consumer! // if on freelist, it is indexed by first member, which is about to be removed! @@ -167,15 +188,7 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm ) } state.members.pop_front(); } else { - GroupState::PositionFifo::iterator pos = state.members.begin() + 1; - GroupState::PositionFifo::iterator end = state.members.end(); - while (pos != end) { - if (*pos == qm.position) { - state.members.erase(pos); - break; - } - ++pos; - } + state.members.erase(m); } uint32_t total = state.members.size(); @@ -220,11 +233,11 @@ bool MessageGroupManager::nextConsumableMessage( Consumer::shared_ptr& c, Queued GroupState& group = findGroup(next); if (!group.owned()) { //TODO: make acquire more efficient when we already have the message in question - if (group.members.front() == next.position && messages.acquire(next.position, next)) { // only take from head! + if (group.members.front().position == next.position && messages.acquire(next.position, next)) { // only take from head! return true; } QPID_LOG(debug, "Skipping " << next.position << " since group " << group.group - << "'s head message still pending. pos=" << group.members.front()); + << "'s head message still pending. pos=" << group.members.front().position); } else if (group.owner == c->getName() && messages.acquire(next.position, next)) { return true; } @@ -284,7 +297,7 @@ void MessageGroupManager::query(qpid::types::Variant::Map& status) const info[GROUP_TIMESTAMP] = 0; if (g->second.members.size() != 0) { QueuedMessage qm; - if (messages.find(g->second.members.front(), qm) && + if (messages.find(g->second.members.front().position, qm) && qm.payload && qm.payload->hasProperties<framing::DeliveryProperties>()) { info[GROUP_TIMESTAMP] = qm.payload->getProperties<framing::DeliveryProperties>()->getTimestamp(); @@ -353,6 +366,7 @@ namespace { const std::string GROUP_OWNER("owner"); const std::string GROUP_ACQUIRED_CT("acquired-ct"); const std::string GROUP_POSITIONS("positions"); + const std::string GROUP_ACQUIRED_MSGS("acquired-msgs"); const std::string GROUP_STATE("group-state"); } @@ -371,10 +385,14 @@ void MessageGroupManager::getState(qpid::framing::FieldTable& state ) const group.setString(GROUP_OWNER, g->second.owner); group.setInt(GROUP_ACQUIRED_CT, g->second.acquired); framing::Array positions(TYPE_CODE_UINT32); - for (GroupState::PositionFifo::const_iterator p = g->second.members.begin(); - p != g->second.members.end(); ++p) - positions.push_back(framing::Array::ValuePtr(new IntegerValue( *p ))); + framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN); + for (GroupState::MessageFifo::const_iterator p = g->second.members.begin(); + p != g->second.members.end(); ++p) { + positions.push_back(framing::Array::ValuePtr(new IntegerValue( p->position ))); + acquiredMsgs.push_back(framing::Array::ValuePtr(new BoolValue( p->acquired ))); + } group.setArray(GROUP_POSITIONS, positions); + group.setArray(GROUP_ACQUIRED_MSGS, acquiredMsgs); groupState.push_back(framing::Array::ValuePtr(new FieldTableValue(group))); } state.setArray(GROUP_STATE, groupState); @@ -425,13 +443,25 @@ void MessageGroupManager::setState(const qpid::framing::FieldTable& state) qName << "\": position encoding error!"); return; } + framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN); + ok = group.getArray(GROUP_ACQUIRED_MSGS, acquiredMsgs); + if (!ok || positions.count() != acquiredMsgs.count()) { + QPID_LOG(error, "Invalid message group state information for queue \"" << + qName << "\": acquired flag encoding error!"); + return; + } + + Array::const_iterator a = acquiredMsgs.begin(); + for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) { + GroupState::MessageState mState((*p)->getIntegerValue<uint32_t, 4>()); + mState.acquired = (*a++)->getIntegerValue<bool>(); + state.members.push_back(mState); + } - for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) - state.members.push_back((*p)->getIntegerValue<uint32_t, 4>()); messageGroups[state.group] = state; if (!state.owned()) { assert(state.members.size()); - freeGroups[state.members.front()] = &messageGroups[state.group]; + freeGroups[state.members.front().position] = &messageGroups[state.group]; } } diff --git a/cpp/src/qpid/broker/MessageGroupManager.h b/cpp/src/qpid/broker/MessageGroupManager.h index f4bffc4760..2dd97ea2ff 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.h +++ b/cpp/src/qpid/broker/MessageGroupManager.h @@ -28,11 +28,14 @@ #include "qpid/broker/MessageDistributor.h" #include "qpid/sys/unordered_map.h" +#include <deque> + namespace qpid { namespace broker { class QueueObserver; class MessageDistributor; +class Messages; class MessageGroupManager : public StatefulQueueObserver, public MessageDistributor { @@ -45,19 +48,29 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu struct GroupState { // note: update getState()/setState() when changing this object's state implementation - typedef std::deque<framing::SequenceNumber> PositionFifo; + + // track which messages are in this group, and if they have been acquired + struct MessageState { + qpid::framing::SequenceNumber position; + bool acquired; + MessageState() : acquired(false) {} + MessageState(const qpid::framing::SequenceNumber& p) : position(p), acquired(false) {} + bool operator<(const MessageState& b) const { return position < b.position; } + }; + typedef std::deque<MessageState> MessageFifo; std::string group; // group identifier std::string owner; // consumer with outstanding acquired messages uint32_t acquired; // count of outstanding acquired messages - PositionFifo members; // msgs belonging to this group + MessageFifo members; // msgs belonging to this group, in enqueue order GroupState() : acquired(0) {} bool owned() const {return !owner.empty();} + MessageFifo::iterator findMsg(const qpid::framing::SequenceNumber &); }; typedef sys::unordered_map<std::string, struct GroupState> GroupMap; - typedef std::map<framing::SequenceNumber, struct GroupState *> GroupFifo; + typedef std::map<qpid::framing::SequenceNumber, struct GroupState *> GroupFifo; GroupMap messageGroups; // index: group name GroupFifo freeGroups; // ordered by oldest free msg diff --git a/cpp/src/qpid/broker/MessageMap.cpp b/cpp/src/qpid/broker/MessageMap.cpp index 048df45434..9b164d4e5c 100644 --- a/cpp/src/qpid/broker/MessageMap.cpp +++ b/cpp/src/qpid/broker/MessageMap.cpp @@ -20,6 +20,7 @@ */ #include "qpid/broker/MessageMap.h" #include "qpid/broker/QueuedMessage.h" +#include "qpid/log/Statement.h" namespace qpid { namespace broker { @@ -27,7 +28,16 @@ namespace { const std::string EMPTY; } -bool MessageMap::deleted(const QueuedMessage&) { return true; } +bool MessageMap::deleted(const QueuedMessage& message) +{ + Ordering::iterator i = messages.find(message.position); + if (i != messages.end()) { + erase(i); + return true; + } else { + return false; + } +} std::string MessageMap::getKey(const QueuedMessage& message) { @@ -38,30 +48,32 @@ std::string MessageMap::getKey(const QueuedMessage& message) size_t MessageMap::size() { - return messages.size(); + size_t count(0); + for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { + if (i->second.status == QueuedMessage::AVAILABLE) ++count; + } + return count; } bool MessageMap::empty() { - return messages.empty(); + return size() == 0;//TODO: more efficient implementation } void MessageMap::release(const QueuedMessage& message) { - std::string key = getKey(message); - Index::iterator i = index.find(key); - if (i == index.end()) { - index[key] = message; - messages[message.position] = message; - } //else message has already been replaced + Ordering::iterator i = messages.find(message.position); + if (i != messages.end() && i->second.status == QueuedMessage::ACQUIRED) { + i->second.status = QueuedMessage::AVAILABLE; + } } bool MessageMap::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { Ordering::iterator i = messages.find(position); - if (i != messages.end()) { + if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) { + i->second.status = QueuedMessage::ACQUIRED; message = i->second; - erase(i); return true; } else { return false; @@ -71,7 +83,7 @@ bool MessageMap::acquire(const framing::SequenceNumber& position, QueuedMessage& bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& message) { Ordering::iterator i = messages.find(position); - if (i != messages.end()) { + if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) { message = i->second; return true; } else { @@ -79,10 +91,10 @@ bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& me } } -bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool) +bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) { Ordering::iterator i = messages.lower_bound(position+1); - if (i != messages.end()) { + if (i != messages.end() && (i->second.status == QueuedMessage::AVAILABLE || (!unacquired && i->second.status == QueuedMessage::ACQUIRED))) { message = i->second; return true; } else { @@ -92,14 +104,14 @@ bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& bool MessageMap::consume(QueuedMessage& message) { - Ordering::iterator i = messages.begin(); - if (i != messages.end()) { - message = i->second; - erase(i); - return true; - } else { - return false; + for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { + if (i->second.status == QueuedMessage::AVAILABLE) { + i->second.status = QueuedMessage::ACQUIRED; + message = i->second; + return true; + } } + return false; } const QueuedMessage& MessageMap::replace(const QueuedMessage& original, const QueuedMessage& update) @@ -115,12 +127,17 @@ bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed) if (result.second) { //there was no previous message for this key; nothing needs to //be removed, just add the message into its correct position - messages[added.position] = added; + QueuedMessage& a = messages[added.position]; + a = added; + a.status = QueuedMessage::AVAILABLE; + QPID_LOG(debug, "Added message at " << a.position); return false; } else { //there is already a message with that key which needs to be replaced removed = result.first->second; result.first->second = replace(result.first->second, added); + result.first->second.status = QueuedMessage::AVAILABLE; + QPID_LOG(debug, "Displaced message at " << removed.position << " with " << result.first->second.position << ": " << result.first->first); return true; } } @@ -128,15 +145,24 @@ bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed) void MessageMap::foreach(Functor f) { for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) { - f(i->second); + if (i->second.status == QueuedMessage::AVAILABLE) f(i->second); } } void MessageMap::removeIf(Predicate p) { - for (Ordering::iterator i = messages.begin(); i != messages.end(); i++) { - if (p(i->second)) { - erase(i); + for (Ordering::iterator i = messages.begin(); i != messages.end();) { + if (i->second.status == QueuedMessage::AVAILABLE && p(i->second)) { + index.erase(getKey(i->second)); + //Note: Removing from messages means that the subsequent + //call to deleted() for the same message will return + //false. At present that is not a problem. If this were + //changed to hold onto the message until dequeued + //(e.g. with REMOVED state), then the erase() below would + //need to take that into account. + messages.erase(i++); + } else { + ++i; } } } diff --git a/cpp/src/qpid/broker/MessageMap.h b/cpp/src/qpid/broker/MessageMap.h index d1b8217f9b..a668450250 100644 --- a/cpp/src/qpid/broker/MessageMap.h +++ b/cpp/src/qpid/broker/MessageMap.h @@ -43,7 +43,7 @@ class MessageMap : public Messages size_t size(); bool empty(); - bool deleted(const QueuedMessage&); + virtual bool deleted(const QueuedMessage&); void release(const QueuedMessage&); virtual bool acquire(const framing::SequenceNumber&, QueuedMessage&); bool find(const framing::SequenceNumber&, QueuedMessage&); diff --git a/cpp/src/qpid/broker/PriorityQueue.cpp b/cpp/src/qpid/broker/PriorityQueue.cpp index d807ef22b1..ab5ec7235a 100644 --- a/cpp/src/qpid/broker/PriorityQueue.cpp +++ b/cpp/src/qpid/broker/PriorityQueue.cpp @@ -3,13 +3,13 @@ * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file + * regarding copyright ownersip. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,96 +22,87 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/QueuedMessage.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" #include <cmath> namespace qpid { namespace broker { -PriorityQueue::PriorityQueue(int l) : +PriorityQueue::PriorityQueue(int l) : levels(l), messages(levels, Deque()), frontLevel(0), haveFront(false), cached(false) {} -bool PriorityQueue::deleted(const QueuedMessage&) { return true; } +bool PriorityQueue::deleted(const QueuedMessage& qm) { + bool deleted = fifo.deleted(qm); + if (deleted) erase(qm); + return deleted; +} size_t PriorityQueue::size() { - size_t total(0); - for (int i = 0; i < levels; ++i) { - total += messages[i].size(); - } - return total; + return fifo.size(); +} + +namespace { +bool before(QueuedMessage* a, QueuedMessage* b) { return *a < *b; } } void PriorityQueue::release(const QueuedMessage& message) { - uint p = getPriorityLevel(message); - messages[p].insert(lower_bound(messages[p].begin(), messages[p].end(), message), message); - clearCache(); + QueuedMessage* qm = fifo.releasePtr(message); + if (qm) { + uint p = getPriorityLevel(message); + messages[p].insert( + lower_bound(messages[p].begin(), messages[p].end(), qm, before), qm); + clearCache(); + } } -bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message, bool remove) -{ - QueuedMessage comp; - comp.position = position; - for (int i = 0; i < levels; ++i) { - if (!messages[i].empty()) { - unsigned long diff = position.getValue() - messages[i].front().position.getValue(); - long maxEnd = diff < messages[i].size() ? diff : messages[i].size(); - Deque::iterator l = lower_bound(messages[i].begin(),messages[i].begin()+maxEnd,comp); - if (l != messages[i].end() && l->position == position) { - message = *l; - if (remove) { - messages[i].erase(l); - clearCache(); - } - return true; - } + +void PriorityQueue::erase(const QueuedMessage& qm) { + size_t i = getPriorityLevel(qm); + if (!messages[i].empty()) { + long diff = qm.position.getValue() - messages[i].front()->position.getValue(); + if (diff < 0) return; + long maxEnd = std::min(size_t(diff), messages[i].size()); + QueuedMessage mutableQm = qm; // need non-const qm for lower_bound + Deque::iterator l = + lower_bound(messages[i].begin(),messages[i].begin()+maxEnd, &mutableQm, before); + if (l != messages[i].end() && (*l)->position == qm.position) { + messages[i].erase(l); + clearCache(); + return; } } - return false; } bool PriorityQueue::acquire(const framing::SequenceNumber& position, QueuedMessage& message) { - return find(position, message, true); + bool acquired = fifo.acquire(position, message); + if (acquired) erase(message); // No longer available + return acquired; } bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message) { - return find(position, message, false); + return fifo.find(position, message); } -bool PriorityQueue::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool) +bool PriorityQueue::browse( + const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired) { - QueuedMessage match; - match.position = position+1; - Deque::iterator lowest; - bool found = false; - for (int i = 0; i < levels; ++i) { - Deque::iterator m = lower_bound(messages[i].begin(), messages[i].end(), match); - if (m != messages[i].end()) { - if (m->position == match.position) { - message = *m; - return true; - } else if (!found || m->position < lowest->position) { - lowest = m; - found = true; - } - } - } - if (found) { - message = *lowest; - } - return found; + return fifo.browse(position, message, unacquired); } bool PriorityQueue::consume(QueuedMessage& message) { if (checkFront()) { - message = messages[frontLevel].front(); + QueuedMessage* pm = messages[frontLevel].front(); messages[frontLevel].pop_front(); clearCache(); + pm->status = QueuedMessage::ACQUIRED; // Updates FIFO index + message = *pm; return true; } else { return false; @@ -120,23 +111,27 @@ bool PriorityQueue::consume(QueuedMessage& message) bool PriorityQueue::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) { - messages[getPriorityLevel(added)].push_back(added); + QueuedMessage* qmp = fifo.pushPtr(added); + messages[getPriorityLevel(added)].push_back(qmp); clearCache(); - return false;//adding a message never causes one to be removed for deque + return false; // Adding a message never causes one to be removed for deque +} + +void PriorityQueue::updateAcquired(const QueuedMessage& acquired) { + fifo.updateAcquired(acquired); } void PriorityQueue::foreach(Functor f) { - for (int i = 0; i < levels; ++i) { - std::for_each(messages[i].begin(), messages[i].end(), f); - } + fifo.foreach(f); } void PriorityQueue::removeIf(Predicate p) { for (int priority = 0; priority < levels; ++priority) { for (Deque::iterator i = messages[priority].begin(); i != messages[priority].end();) { - if (p(*i)) { + if (p(**i)) { + (*i)->status = QueuedMessage::DELETED; // Updates fifo index i = messages[priority].erase(i); clearCache(); } else { @@ -144,6 +139,7 @@ void PriorityQueue::removeIf(Predicate p) } } } + fifo.clean(); } uint PriorityQueue::getPriorityLevel(const QueuedMessage& m) const diff --git a/cpp/src/qpid/broker/PriorityQueue.h b/cpp/src/qpid/broker/PriorityQueue.h index 67c31468d2..8628745db1 100644 --- a/cpp/src/qpid/broker/PriorityQueue.h +++ b/cpp/src/qpid/broker/PriorityQueue.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -21,7 +21,7 @@ * under the License. * */ -#include "qpid/broker/Messages.h" +#include "qpid/broker/MessageDeque.h" #include "qpid/sys/IntegerTypes.h" #include <deque> #include <vector> @@ -32,7 +32,10 @@ namespace broker { /** * Basic priority queue with a configurable number of recognised * priority levels. This is implemented as a separate deque per - * priority level. Browsing is FIFO not priority order. + * priority level. + * + * Browsing is FIFO not priority order. There is a MessageDeque + * for fast browsing. */ class PriorityQueue : public Messages { @@ -48,23 +51,31 @@ class PriorityQueue : public Messages bool browse(const framing::SequenceNumber&, QueuedMessage&, bool); bool consume(QueuedMessage&); bool push(const QueuedMessage& added, QueuedMessage& removed); - + void updateAcquired(const QueuedMessage& acquired); void foreach(Functor); void removeIf(Predicate); + static uint getPriority(const QueuedMessage&); + protected: - typedef std::deque<QueuedMessage> Deque; + typedef std::deque<QueuedMessage*> Deque; typedef std::vector<Deque> PriorityLevels; virtual bool findFrontLevel(uint& p, PriorityLevels&); const int levels; + private: + /** Available messages separated by priority and sorted in priority order. + * Holds pointers to the QueuedMessages in fifo + */ PriorityLevels messages; + /** FIFO index of all messsagse (including acquired messages) for fast browsing and indexing */ + MessageDeque fifo; uint frontLevel; bool haveFront; bool cached; - - bool find(const framing::SequenceNumber&, QueuedMessage&, bool remove); + + void erase(const QueuedMessage&); uint getPriorityLevel(const QueuedMessage&) const; void clearCache(); bool checkFront(); diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp index 015957927f..e7305c021d 100644 --- a/cpp/src/qpid/broker/Queue.cpp +++ b/cpp/src/qpid/broker/Queue.cpp @@ -19,8 +19,9 @@ * */ -#include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" + +#include "qpid/broker/Broker.h" #include "qpid/broker/QueueEvents.h" #include "qpid/broker/Exchange.h" #include "qpid/broker/Fairshare.h" @@ -41,6 +42,7 @@ #include "qpid/management/ManagementAgent.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/sys/ClusterSafe.h" #include "qpid/sys/Monitor.h" #include "qpid/sys/Time.h" @@ -56,7 +58,9 @@ #include <boost/intrusive_ptr.hpp> -using namespace qpid::broker; +namespace qpid { +namespace broker { + using namespace qpid::sys; using namespace qpid::framing; using qpid::management::ManagementAgent; @@ -88,8 +92,57 @@ const std::string qpidInsertSequenceNumbers("qpid.insert_sequence_numbers"); const int ENQUEUE_ONLY=1; const int ENQUEUE_AND_DEQUEUE=2; + +inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg, + _qmf::Queue* mgmtObject, + _qmf::Broker* brokerMgmtObject) +{ + if (mgmtObject != 0) { + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + + uint64_t contentSize = msg->contentSize(); + qStats->msgTotalEnqueues +=1; + bStats->msgTotalEnqueues += 1; + qStats->byteTotalEnqueues += contentSize; + bStats->byteTotalEnqueues += contentSize; + if (msg->isPersistent ()) { + qStats->msgPersistEnqueues += 1; + bStats->msgPersistEnqueues += 1; + qStats->bytePersistEnqueues += contentSize; + bStats->bytePersistEnqueues += contentSize; + } + mgmtObject->statisticsUpdated(); + brokerMgmtObject->statisticsUpdated(); + } +} + +inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg, + _qmf::Queue* mgmtObject, + _qmf::Broker* brokerMgmtObject) +{ + if (mgmtObject != 0){ + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + uint64_t contentSize = msg->contentSize(); + + qStats->msgTotalDequeues += 1; + bStats->msgTotalDequeues += 1; + qStats->byteTotalDequeues += contentSize; + bStats->byteTotalDequeues += contentSize; + if (msg->isPersistent ()){ + qStats->msgPersistDequeues += 1; + bStats->msgPersistDequeues += 1; + qStats->bytePersistDequeues += contentSize; + bStats->bytePersistDequeues += contentSize; + } + mgmtObject->statisticsUpdated(); + brokerMgmtObject->statisticsUpdated(); + } } +} // namespace + Queue::Queue(const string& _name, bool _autodelete, MessageStore* const _store, const OwnershipToken* const _owner, @@ -101,6 +154,7 @@ Queue::Queue(const string& _name, bool _autodelete, store(_store), owner(_owner), consumerCount(0), + browserCount(0), exclusive(0), noLocal(false), persistLastNode(false), @@ -166,7 +220,7 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){ if (msg->isImmediate() && getConsumerCount() == 0) { if (alternateExchange) { DeliverableMessage deliverable(msg); - alternateExchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders()); + alternateExchange->route(deliverable); } } else if (isLocal(msg)) { //drop message @@ -183,11 +237,16 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){ void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg) { + Mutex::ScopedLock locker(messageLock); if (policy.get()) policy->recoverEnqueued(msg); } -void Queue::recover(boost::intrusive_ptr<Message>& msg){ - if (policy.get()) policy->recoverEnqueued(msg); +void Queue::recover(boost::intrusive_ptr<Message>& msg) +{ + { + Mutex::ScopedLock locker(messageLock); + if (policy.get()) policy->recoverEnqueued(msg); + } push(msg, true); if (store){ @@ -209,11 +268,16 @@ void Queue::recover(boost::intrusive_ptr<Message>& msg){ void Queue::process(boost::intrusive_ptr<Message>& msg){ push(msg); if (mgmtObject != 0){ - mgmtObject->inc_msgTxnEnqueues (); - mgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + const uint64_t contentSize = msg->contentSize(); + qStats->msgTxnEnqueues += 1; + qStats->byteTxnEnqueues += contentSize; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgTxnEnqueues (); - brokerMgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgTxnEnqueues += 1; + bStats->byteTxnEnqueues += contentSize; + brokerMgmtObject->statisticsUpdated(); } } } @@ -222,7 +286,6 @@ void Queue::requeue(const QueuedMessage& msg){ assertClusterSafe(); QueueListeners::NotificationSet copy; { - Mutex::ScopedLock locker(messageLock); if (!isEnqueued(msg)) return; if (deleted) { // @@ -238,10 +301,20 @@ void Queue::requeue(const QueuedMessage& msg){ if (brokerMgmtObject) brokerMgmtObject->inc_abandoned(); } - mgntDeqStats(msg.payload); + mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); } else { - messages->release(msg); - listeners.populate(copy); + { + Mutex::ScopedLock locker(messageLock); + messages->release(msg); + observeRequeue(msg, locker); + listeners.populate(copy); + } + + if (mgmtObject) { + mgmtObject->inc_releases(); + if (brokerMgmtObject) + brokerMgmtObject->inc_releases(); + } // for persistLastNode - don't force a message twice to disk, but force it if no force before if(inLastNodeFailure && persistLastNode && !msg.payload->isStoredOnQueue(shared_from_this())) { @@ -251,7 +324,6 @@ void Queue::requeue(const QueuedMessage& msg){ enqueue(0, payload); } } - observeRequeue(msg, locker); } } copy.notify(); @@ -259,10 +331,9 @@ void Queue::requeue(const QueuedMessage& msg){ bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message) { - Mutex::ScopedLock locker(messageLock); assertClusterSafe(); QPID_LOG(debug, "Attempting to acquire message at " << position); - if (acquire(position, message, locker)) { + if (acquire(position, message)) { QPID_LOG(debug, "Acquired message at " << position << " from " << name); return true; } else { @@ -273,17 +344,20 @@ bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& mess bool Queue::acquire(const QueuedMessage& msg, const std::string& consumer) { - Mutex::ScopedLock locker(messageLock); assertClusterSafe(); QPID_LOG(debug, consumer << " attempting to acquire message at " << msg.position); - - if (!allocator->allocate( consumer, msg )) { + bool ok; + { + Mutex::ScopedLock locker(messageLock); + ok = allocator->allocate( consumer, msg ); + } + if (!ok) { QPID_LOG(debug, "Not permitted to acquire msg at " << msg.position << " from '" << name); return false; } QueuedMessage copy(msg); - if (acquire( msg.position, copy, locker)) { + if (acquire( msg.position, copy)) { QPID_LOG(debug, "Acquired message at " << msg.position << " from " << name); return true; } @@ -325,59 +399,73 @@ bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) { while (true) { - Mutex::ScopedLock locker(messageLock); QueuedMessage msg; - if (allocator->nextConsumableMessage(c, msg)) { - if (msg.payload->hasExpired()) { - QPID_LOG(debug, "Message expired from queue '" << name << "'"); - c->setPosition(msg.position); - dequeue(0, msg); - if (mgmtObject) { - mgmtObject->inc_discardsTtl(); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsTtl(); - } + bool found; + { + Mutex::ScopedLock locker(messageLock); + found = allocator->nextConsumableMessage(c, msg); + if (!found) listeners.addListener(c); + } + if (!found) { + QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); + return NO_MESSAGES; + } - continue; + if (msg.payload->hasExpired()) { + QPID_LOG(debug, "Message expired from queue '" << name << "'"); + c->setPosition(msg.position); + dequeue(0, msg); + if (mgmtObject) { + mgmtObject->inc_discardsTtl(); + if (brokerMgmtObject) + brokerMgmtObject->inc_discardsTtl(); } + continue; + } - if (c->filter(msg.payload)) { - if (c->accept(msg.payload)) { + if (c->filter(msg.payload)) { + if (c->accept(msg.payload)) { + { + Mutex::ScopedLock locker(messageLock); bool ok = allocator->allocate( c->getName(), msg ); // inform allocator (void) ok; assert(ok); observeAcquire(msg, locker); - m = msg; - return CONSUMED; - } else { - //message(s) are available but consumer hasn't got enough credit - QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); - messages->release(msg); - return CANT_CONSUME; } + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } + m = msg; + return CONSUMED; } else { - //consumer will never want this message - QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); - messages->release(msg); - return CANT_CONSUME; + //message(s) are available but consumer hasn't got enough credit + QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); } } else { - QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); - listeners.addListener(c); - return NO_MESSAGES; + //consumer will never want this message + QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); } + + Mutex::ScopedLock locker(messageLock); + messages->release(msg); + return CANT_CONSUME; } } bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr& c) { while (true) { - Mutex::ScopedLock locker(messageLock); QueuedMessage msg; - - if (!allocator->nextBrowsableMessage(c, msg)) { // no next available + bool found; + { + Mutex::ScopedLock locker(messageLock); + found = allocator->nextBrowsableMessage(c, msg); + if (!found) listeners.addListener(c); + } + if (!found) { // no next available QPID_LOG(debug, "No browsable messages available for consumer " << c->getName() << " on queue '" << name << "'"); - listeners.addListener(c); return false; } @@ -435,60 +523,67 @@ bool Queue::find(SequenceNumber pos, QueuedMessage& msg) const { void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ assertClusterSafe(); { - Mutex::ScopedLock locker(consumerLock); - if(exclusive) { - throw ResourceLockedException( - QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed.")); - } else if(requestExclusive) { - if(consumerCount) { + Mutex::ScopedLock locker(messageLock); + // NOTE: consumerCount is actually a count of all + // subscriptions, both acquiring and non-acquiring (browsers). + // Check for exclusivity of acquiring consumers. + size_t acquiringConsumers = consumerCount - browserCount; + if (c->preAcquires()) { + if(exclusive) { throw ResourceLockedException( - QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied.")); - } else { - exclusive = c->getSession(); + QPID_MSG("Queue " << getName() + << " has an exclusive consumer. No more consumers allowed.")); + } else if(requestExclusive) { + if(acquiringConsumers) { + throw ResourceLockedException( + QPID_MSG("Queue " << getName() + << " already has consumers. Exclusive access denied.")); + } else { + exclusive = c->getSession(); + } } } + else + browserCount++; consumerCount++; - if (mgmtObject != 0) - mgmtObject->inc_consumerCount (); //reset auto deletion timer if necessary if (autoDeleteTimeout && autoDeleteTask) { autoDeleteTask->cancel(); } + observeConsumerAdd(*c, locker); } - Mutex::ScopedLock locker(messageLock); - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { - try{ - (*i)->consumerAdded(*c); - } catch (const std::exception& e) { - QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what()); - } - } + if (mgmtObject != 0) + mgmtObject->inc_consumerCount (); } void Queue::cancel(Consumer::shared_ptr c){ removeListener(c); { - Mutex::ScopedLock locker(consumerLock); + Mutex::ScopedLock locker(messageLock); consumerCount--; + if (!c->preAcquires()) browserCount--; if(exclusive) exclusive = 0; - if (mgmtObject != 0) - mgmtObject->dec_consumerCount (); - } - Mutex::ScopedLock locker(messageLock); - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { - try{ - (*i)->consumerRemoved(*c); - } catch (const std::exception& e) { - QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what()); - } + observeConsumerRemove(*c, locker); } + if (mgmtObject != 0) + mgmtObject->dec_consumerCount (); } QueuedMessage Queue::get(){ - Mutex::ScopedLock locker(messageLock); QueuedMessage msg(this); - if (messages->consume(msg)) - observeAcquire(msg, locker); + bool ok; + { + Mutex::ScopedLock locker(messageLock); + ok = messages->consume(msg); + if (ok) observeAcquire(msg, locker); + } + + if (ok && mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } + return msg; } @@ -520,22 +615,26 @@ void Queue::purgeExpired(qpid::sys::Duration lapse) messages->removeIf(boost::bind(&collect_if_expired, boost::ref(expired), _1)); } - // - // Report the count of discarded-by-ttl messages - // - if (mgmtObject && !expired.empty()) { - mgmtObject->inc_discardsTtl(expired.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsTtl(expired.size()); - } + if (!expired.empty()) { + if (mgmtObject) { + mgmtObject->inc_acquires(expired.size()); + mgmtObject->inc_discardsTtl(expired.size()); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(expired.size()); + brokerMgmtObject->inc_discardsTtl(expired.size()); + } + } - for (std::deque<QueuedMessage>::const_iterator i = expired.begin(); - i != expired.end(); ++i) { - { - Mutex::ScopedLock locker(messageLock); - observeAcquire(*i, locker); + for (std::deque<QueuedMessage>::const_iterator i = expired.begin(); + i != expired.end(); ++i) { + { + // KAG: should be safe to retake lock after the removeIf, since + // no other thread can touch these messages after the removeIf() call + Mutex::ScopedLock locker(messageLock); + observeAcquire(*i, locker); + } + dequeue( 0, *i ); } - dequeue( 0, *i ); } } } @@ -661,32 +760,46 @@ uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter)); Collector c(*mf.get(), purge_request); - Mutex::ScopedLock locker(messageLock); - messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + { + Mutex::ScopedLock locker(messageLock); + messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + } - if (mgmtObject && !c.matches.empty()) { - if (dest.get()) { - mgmtObject->inc_reroutes(c.matches.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_reroutes(c.matches.size()); - } else { - mgmtObject->inc_discardsPurge(c.matches.size()); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsPurge(c.matches.size()); + if (!c.matches.empty()) { + if (mgmtObject) { + mgmtObject->inc_acquires(c.matches.size()); + if (dest.get()) { + mgmtObject->inc_reroutes(c.matches.size()); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(c.matches.size()); + brokerMgmtObject->inc_reroutes(c.matches.size()); + } + } else { + mgmtObject->inc_discardsPurge(c.matches.size()); + if (brokerMgmtObject) { + brokerMgmtObject->inc_acquires(c.matches.size()); + brokerMgmtObject->inc_discardsPurge(c.matches.size()); + } + } } - } - for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); - qmsg != c.matches.end(); ++qmsg) { - // Update observers and message state: - observeAcquire(*qmsg, locker); - dequeue(0, *qmsg); - QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName()); - // now reroute if necessary - if (dest.get()) { - assert(qmsg->payload); - DeliverableMessage dmsg(qmsg->payload); - dest->routeWithAlternate(dmsg); + for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); + qmsg != c.matches.end(); ++qmsg) { + + { + // KAG: should be safe to retake lock after the removeIf, since + // no other thread can touch these messages after the removeIf call + Mutex::ScopedLock locker(messageLock); + observeAcquire(*qmsg, locker); + } + dequeue(0, *qmsg); + QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName()); + // now reroute if necessary + if (dest.get()) { + assert(qmsg->payload); + DeliverableMessage dmsg(qmsg->payload); + dest->routeWithAlternate(dmsg); + } } } return c.matches.size(); @@ -698,27 +811,51 @@ uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty, std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter)); Collector c(*mf.get(), qty); - Mutex::ScopedLock locker(messageLock); - messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + { + Mutex::ScopedLock locker(messageLock); + messages->removeIf( boost::bind<bool>(boost::ref(c), _1) ); + } + - for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); - qmsg != c.matches.end(); ++qmsg) { + if (!c.matches.empty()) { // Update observers and message state: - observeAcquire(*qmsg, locker); - dequeue(0, *qmsg); - // and move to destination Queue. - assert(qmsg->payload); - destq->deliver(qmsg->payload); + + if (mgmtObject) { + mgmtObject->inc_acquires(c.matches.size()); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(c.matches.size()); + } + + for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin(); + qmsg != c.matches.end(); ++qmsg) { + { + Mutex::ScopedLock locker(messageLock); + observeAcquire(*qmsg, locker); + } + dequeue(0, *qmsg); + // and move to destination Queue. + assert(qmsg->payload); + destq->deliver(qmsg->payload); + } } return c.matches.size(); } /** Acquire the message at the given position, return true and msg if acquire succeeds */ -bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg, - const Mutex::ScopedLock& locker) +bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg) { - if (messages->acquire(position, msg)) { - observeAcquire(msg, locker); + bool ok; + { + Mutex::ScopedLock locker(messageLock); + ok = messages->acquire(position, msg); + if (ok) observeAcquire(msg, locker); + } + if (ok) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } ++dequeueSincePurge; return true; } @@ -728,35 +865,43 @@ bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){ assertClusterSafe(); QueueListeners::NotificationSet copy; - QueuedMessage removed; + QueuedMessage removed, qm(this, msg); bool dequeueRequired = false; { Mutex::ScopedLock locker(messageLock); - QueuedMessage qm(this, msg, ++sequence); - if (insertSeqNo) msg->insertCustomProperty(seqNoKey, sequence); - - dequeueRequired = messages->push(qm, removed); - if (dequeueRequired) { + qm.position = ++sequence; + if (messages->push(qm, removed)) { + dequeueRequired = true; observeAcquire(removed, locker); - if (mgmtObject) { - mgmtObject->inc_discardsLvq(); - if (brokerMgmtObject) - brokerMgmtObject->inc_discardsLvq(); - } } - listeners.populate(copy); observeEnqueue(qm, locker); + if (policy.get()) { + policy->enqueued(qm); + } + listeners.populate(copy); } - copy.notify(); + if (insertSeqNo) msg->insertCustomProperty(seqNoKey, qm.position); + + mgntEnqStats(msg, mgmtObject, brokerMgmtObject); + if (dequeueRequired) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + mgmtObject->inc_discardsLvq(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + brokerMgmtObject->inc_discardsLvq(); + } if (isRecovery) { //can't issue new requests for the store until //recovery is complete + Mutex::ScopedLock locker(messageLock); pendingDequeues.push_back(removed); } else { dequeue(0, removed); } } + copy.notify(); } void isEnqueueComplete(uint32_t* result, const QueuedMessage& message) @@ -767,8 +912,8 @@ void isEnqueueComplete(uint32_t* result, const QueuedMessage& message) /** function only provided for unit tests, or code not in critical message path */ uint32_t Queue::getEnqueueCompleteMessageCount() const { - Mutex::ScopedLock locker(messageLock); uint32_t count = 0; + Mutex::ScopedLock locker(messageLock); messages->foreach(boost::bind(&isEnqueueComplete, &count, _1)); return count; } @@ -781,13 +926,13 @@ uint32_t Queue::getMessageCount() const uint32_t Queue::getConsumerCount() const { - Mutex::ScopedLock locker(consumerLock); + Mutex::ScopedLock locker(messageLock); return consumerCount; } bool Queue::canAutoDelete() const { - Mutex::ScopedLock locker(consumerLock); + Mutex::ScopedLock locker(messageLock); return autodelete && !consumerCount && !owner; } @@ -894,14 +1039,20 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) { ScopedUse u(barrier); if (!u.acquired) return false; - { Mutex::ScopedLock locker(messageLock); if (!isEnqueued(msg)) return false; if (!ctxt) { + if (policy.get()) policy->dequeued(msg); + messages->deleted(msg); observeDequeue(msg, locker); } } + + if (!ctxt) { + mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); + } + // This check prevents messages which have been forced persistent on one queue from dequeuing // from another on which no forcing has taken place and thus causing a store error. bool fp = msg.payload->isForcedPersistent(); @@ -918,14 +1069,24 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) void Queue::dequeueCommitted(const QueuedMessage& msg) { - Mutex::ScopedLock locker(messageLock); - observeDequeue(msg, locker); + { + Mutex::ScopedLock locker(messageLock); + if (policy.get()) policy->dequeued(msg); + messages->deleted(msg); + observeDequeue(msg, locker); + } + mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject); if (mgmtObject != 0) { - mgmtObject->inc_msgTxnDequeues(); - mgmtObject->inc_byteTxnDequeues(msg.payload->contentSize()); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + const uint64_t contentSize = msg.payload->contentSize(); + qStats->msgTxnDequeues += 1; + qStats->byteTxnDequeues += contentSize; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgTxnDequeues(); - brokerMgmtObject->inc_byteTxnDequeues(msg.payload->contentSize()); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgTxnDequeues += 1; + bStats->byteTxnDequeues += contentSize; + brokerMgmtObject->statisticsUpdated(); } } } @@ -934,10 +1095,20 @@ void Queue::dequeueCommitted(const QueuedMessage& msg) * Removes the first (oldest) message from the in-memory delivery queue as well dequeing * it from the logical (and persistent if applicable) queue */ -bool Queue::popAndDequeue(QueuedMessage& msg, const Mutex::ScopedLock& locker) +bool Queue::popAndDequeue(QueuedMessage& msg) { - if (messages->consume(msg)) { - observeAcquire(msg, locker); + bool popped; + { + Mutex::ScopedLock locker(messageLock); + popped = messages->consume(msg); + if (popped) observeAcquire(msg, locker); + } + if (popped) { + if (mgmtObject) { + mgmtObject->inc_acquires(); + if (brokerMgmtObject) + brokerMgmtObject->inc_acquires(); + } dequeue(0, msg); return true; } else { @@ -947,13 +1118,10 @@ bool Queue::popAndDequeue(QueuedMessage& msg, const Mutex::ScopedLock& locker) /** * Updates policy and management when a message has been dequeued, - * expects messageLock to be held + * Requires messageLock be held by caller. */ -void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) +void Queue::observeDequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) { - mgntDeqStats(msg.payload); - if (policy.get()) policy->dequeued(msg); - messages->deleted(msg); for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->dequeued(msg); @@ -963,17 +1131,11 @@ void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) } } -/** updates queue observers when a message has become unavailable for transfer, - * expects messageLock to be held +/** updates queue observers when a message has become unavailable for transfer. + * Requires messageLock be held by caller. */ -void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&) +void Queue::observeAcquire(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) { - if (mgmtObject) { - mgmtObject->inc_acquires(); - if (brokerMgmtObject) - brokerMgmtObject->inc_acquires(); - } - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->acquired(msg); @@ -983,17 +1145,11 @@ void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&) } } -/** updates queue observers when a message has become re-available for transfer, - * expects messageLock to be held +/** updates queue observers when a message has become re-available for transfer + * Requires messageLock be held by caller. */ -void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) +void Queue::observeRequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&) { - if (mgmtObject) { - mgmtObject->inc_releases(); - if (brokerMgmtObject) - brokerMgmtObject->inc_releases(); - } - for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { try{ (*i)->requeued(msg); @@ -1003,6 +1159,33 @@ void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&) } } +/** updates queue observers when a new consumer has subscribed to this queue. + */ +void Queue::observeConsumerAdd( const Consumer& c, const qpid::sys::Mutex::ScopedLock&) +{ + for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { + try{ + (*i)->consumerAdded(c); + } catch (const std::exception& e) { + QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what()); + } + } +} + +/** updates queue observers when a consumer has unsubscribed from this queue. + */ +void Queue::observeConsumerRemove( const Consumer& c, const qpid::sys::Mutex::ScopedLock&) +{ + for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) { + try{ + (*i)->consumerRemoved(c); + } catch (const std::exception& e) { + QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what()); + } + } +} + + void Queue::create(const FieldTable& _settings) { settings = _settings; @@ -1150,23 +1333,21 @@ void Queue::configureImpl(const FieldTable& _settings) void Queue::destroyed() { unbind(broker->getExchanges()); - { - Mutex::ScopedLock locker(messageLock); - QueuedMessage m; - while(popAndDequeue(m, locker)) { - DeliverableMessage msg(m.payload); - if (alternateExchange.get()) { - if (brokerMgmtObject) - brokerMgmtObject->inc_abandonedViaAlt(); - alternateExchange->routeWithAlternate(msg); - } else { - if (brokerMgmtObject) - brokerMgmtObject->inc_abandoned(); - } + + QueuedMessage m; + while(popAndDequeue(m)) { + DeliverableMessage msg(m.payload); + if (alternateExchange.get()) { + if (brokerMgmtObject) + brokerMgmtObject->inc_abandonedViaAlt(); + alternateExchange->routeWithAlternate(msg); + } else { + if (brokerMgmtObject) + brokerMgmtObject->inc_abandoned(); } - if (alternateExchange.get()) - alternateExchange->decAlternateUsers(); } + if (alternateExchange.get()) + alternateExchange->decAlternateUsers(); if (store) { barrier.destroy(); @@ -1177,7 +1358,7 @@ void Queue::destroyed() if (autoDeleteTask) autoDeleteTask = boost::intrusive_ptr<TimerTask>(); notifyDeleted(); { - Mutex::ScopedLock locker(messageLock); + Mutex::ScopedLock lock(messageLock); observers.clear(); } } @@ -1187,8 +1368,8 @@ void Queue::notifyDeleted() QueueListeners::ListenerSet set; { Mutex::ScopedLock locker(messageLock); - listeners.snapshot(set); deleted = true; + listeners.snapshot(set); } set.notifyAll(); } @@ -1206,6 +1387,7 @@ void Queue::unbind(ExchangeRegistry& exchanges) void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) { + Mutex::ScopedLock locker(messageLock); policy = _policy; if (policy.get()) policy->setQueue(this); @@ -1213,6 +1395,7 @@ void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) const QueuePolicy* Queue::getPolicy() { + Mutex::ScopedLock locker(messageLock); return policy.get(); } @@ -1302,7 +1485,7 @@ struct AutoDeleteTask : qpid::sys::TimerTask Queue::shared_ptr queue; AutoDeleteTask(Broker& b, Queue::shared_ptr q, AbsTime fireTime) - : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion"), broker(b), queue(q) {} + : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion:"+q->getName()), broker(b), queue(q) {} void fire() { @@ -1388,11 +1571,15 @@ void Queue::countRejected() const void Queue::countFlowedToDisk(uint64_t size) const { if (mgmtObject) { - mgmtObject->inc_msgFtdEnqueues(); - mgmtObject->inc_byteFtdEnqueues(size); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + qStats->msgFtdEnqueues += 1; + qStats->byteFtdEnqueues += size; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgFtdEnqueues(); - brokerMgmtObject->inc_byteFtdEnqueues(size); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgFtdEnqueues += 1; + bStats->byteFtdEnqueues += size; + brokerMgmtObject->statisticsUpdated(); } } } @@ -1400,11 +1587,15 @@ void Queue::countFlowedToDisk(uint64_t size) const void Queue::countLoadedFromDisk(uint64_t size) const { if (mgmtObject) { - mgmtObject->inc_msgFtdDequeues(); - mgmtObject->inc_byteFtdDequeues(size); + _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); + qStats->msgFtdDequeues += 1; + qStats->byteFtdDequeues += size; + mgmtObject->statisticsUpdated(); if (brokerMgmtObject) { - brokerMgmtObject->inc_msgFtdDequeues(); - brokerMgmtObject->inc_byteFtdDequeues(size); + _qmf::Broker::PerThreadStats *bStats = brokerMgmtObject->getStatistics(); + bStats->msgFtdDequeues += 1; + bStats->byteFtdDequeues += size; + brokerMgmtObject->statisticsUpdated(); } } } @@ -1434,9 +1625,14 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, str { _qmf::ArgsQueueReroute& rerouteArgs = (_qmf::ArgsQueueReroute&) args; boost::shared_ptr<Exchange> dest; - if (rerouteArgs.i_useAltExchange) + if (rerouteArgs.i_useAltExchange) { + if (!alternateExchange) { + status = Manageable::STATUS_PARAMETER_INVALID; + etext = "No alternate-exchange defined"; + break; + } dest = alternateExchange; - else { + } else { try { dest = broker->getExchanges().get(rerouteArgs.i_exchange); } catch(const std::exception&) { @@ -1486,8 +1682,12 @@ void Queue::recoveryComplete(ExchangeRegistry& exchanges) << "\": exchange does not exist."); } //process any pending dequeues - for_each(pendingDequeues.begin(), pendingDequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); - pendingDequeues.clear(); + std::deque<QueuedMessage> pd; + { + Mutex::ScopedLock locker(messageLock); + pendingDequeues.swap(pd); + } + for_each(pd.begin(), pd.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); } void Queue::insertSequenceNumbers(const std::string& key) @@ -1497,10 +1697,10 @@ void Queue::insertSequenceNumbers(const std::string& key) QPID_LOG(debug, "Inserting sequence numbers as " << key); } -/** updates queue observers and state when a message has become available for transfer, - * expects messageLock to be held +/** updates queue observers and state when a message has become available for transfer + * Requires messageLock be held by caller. */ -void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&) +void Queue::observeEnqueue(const QueuedMessage& m, const qpid::sys::Mutex::ScopedLock&) { for (Observers::iterator i = observers.begin(); i != observers.end(); ++i) { try { @@ -1509,10 +1709,6 @@ void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&) QPID_LOG(warning, "Exception on notification of enqueue for queue " << getName() << ": " << e.what()); } } - if (policy.get()) { - policy->enqueued(m); - } - mgntEnqStats(m.payload); } void Queue::updateEnqueued(const QueuedMessage& m) @@ -1520,12 +1716,16 @@ void Queue::updateEnqueued(const QueuedMessage& m) if (m.payload) { boost::intrusive_ptr<Message> payload = m.payload; enqueue(0, payload, true); - messages->updateAcquired(m); - if (policy.get()) { - policy->recoverEnqueued(payload); + { + Mutex::ScopedLock locker(messageLock); + messages->updateAcquired(m); + observeEnqueue(m, locker); + if (policy.get()) { + policy->recoverEnqueued(payload); + policy->enqueued(m); + } } - Mutex::ScopedLock locker(messageLock); - observeEnqueue(m, locker); + mgntEnqStats(m.payload, mgmtObject, brokerMgmtObject); } else { QPID_LOG(warning, "Queue informed of enqueued message that has no payload"); } @@ -1533,10 +1733,16 @@ void Queue::updateEnqueued(const QueuedMessage& m) bool Queue::isEnqueued(const QueuedMessage& msg) { + Mutex::ScopedLock locker(messageLock); return !policy.get() || policy->isEnqueued(msg); } +// Note: accessing listeners outside of lock is dangerous. Caller must ensure the queue's +// state is not changed while listeners is referenced. QueueListeners& Queue::getListeners() { return listeners; } + +// Note: accessing messages outside of lock is dangerous. Caller must ensure the queue's +// state is not changed while messages is referenced. Messages& Queue::getMessages() { return *messages; } const Messages& Queue::getMessages() const { return *messages; } @@ -1549,13 +1755,13 @@ void Queue::checkNotDeleted(const Consumer::shared_ptr& c) void Queue::addObserver(boost::shared_ptr<QueueObserver> observer) { - Mutex::ScopedLock locker(messageLock); + Mutex::ScopedLock lock(messageLock); observers.insert(observer); } void Queue::removeObserver(boost::shared_ptr<QueueObserver> observer) { - Mutex::ScopedLock locker(messageLock); + Mutex::ScopedLock lock(messageLock); observers.erase(observer); } @@ -1618,7 +1824,7 @@ Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {} bool Queue::UsageBarrier::acquire() { - Monitor::ScopedLock l(parent.messageLock); + Monitor::ScopedLock l(parent.messageLock); /** @todo: use a dedicated lock instead of messageLock */ if (parent.deleted) { return false; } else { @@ -1639,3 +1845,6 @@ void Queue::UsageBarrier::destroy() parent.deleted = true; while (count) parent.messageLock.wait(); } + +}} + diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h index e8573c17cc..9869a698c1 100644 --- a/cpp/src/qpid/broker/Queue.h +++ b/cpp/src/qpid/broker/Queue.h @@ -97,7 +97,8 @@ class Queue : public boost::enable_shared_from_this<Queue>, const bool autodelete; MessageStore* store; const OwnershipToken* owner; - uint32_t consumerCount; + uint32_t consumerCount; // Actually a count of all subscriptions, acquiring or not. + uint32_t browserCount; // Count of non-acquiring subscriptions. OwnershipToken* exclusive; bool noLocal; bool persistLastNode; @@ -107,7 +108,22 @@ class Queue : public boost::enable_shared_from_this<Queue>, QueueListeners listeners; std::auto_ptr<Messages> messages; std::deque<QueuedMessage> pendingDequeues;//used to avoid dequeuing during recovery - mutable qpid::sys::Mutex consumerLock; + /** messageLock is used to keep the Queue's state consistent while processing message + * events, such as message dispatch, enqueue, acquire, and dequeue. It must be held + * while updating certain members in order to keep these members consistent with + * each other: + * o messages + * o sequence + * o policy + * o listeners + * o allocator + * o observeXXX() methods + * o observers + * o pendingDequeues (TBD: move under separate lock) + * o exclusive OwnershipToken (TBD: move under separate lock) + * o consumerCount (TBD: move under separate lock) + * o Queue::UsageBarrier (TBD: move under separate lock) + */ mutable qpid::sys::Monitor messageLock; mutable qpid::sys::Mutex ownershipLock; mutable uint64_t persistenceId; @@ -143,52 +159,20 @@ class Queue : public boost::enable_shared_from_this<Queue>, bool isExcluded(boost::intrusive_ptr<Message>& msg); - /** update queue observers, stats, policy, etc when the messages' state changes. Lock - * must be held by caller */ + /** update queue observers, stats, policy, etc when the messages' state changes. + * messageLock is held by caller */ void observeEnqueue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); void observeAcquire(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); void observeRequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); void observeDequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock); - bool popAndDequeue(QueuedMessage&, const sys::Mutex::ScopedLock& lock); - // acquire message @ position, return true and set msg if acquire succeeds - bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg, - const sys::Mutex::ScopedLock& held); + void observeConsumerAdd( const Consumer&, const sys::Mutex::ScopedLock& lock); + void observeConsumerRemove( const Consumer&, const sys::Mutex::ScopedLock& lock); + bool popAndDequeue(QueuedMessage&); + bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg); void forcePersistent(QueuedMessage& msg); int getEventMode(); void configureImpl(const qpid::framing::FieldTable& settings); - - inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg) - { - if (mgmtObject != 0) { - mgmtObject->inc_msgTotalEnqueues (); - mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - brokerMgmtObject->inc_msgTotalEnqueues (); - brokerMgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - if (msg->isPersistent ()) { - mgmtObject->inc_msgPersistEnqueues (); - mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); - brokerMgmtObject->inc_msgPersistEnqueues (); - brokerMgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); - } - } - } - inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg) - { - if (mgmtObject != 0){ - mgmtObject->inc_msgTotalDequeues (); - mgmtObject->inc_byteTotalDequeues (msg->contentSize()); - brokerMgmtObject->inc_msgTotalDequeues (); - brokerMgmtObject->inc_byteTotalDequeues (msg->contentSize()); - if (msg->isPersistent ()){ - mgmtObject->inc_msgPersistDequeues (); - mgmtObject->inc_bytePersistDequeues (msg->contentSize()); - brokerMgmtObject->inc_msgPersistDequeues (); - brokerMgmtObject->inc_bytePersistDequeues (msg->contentSize()); - } - } - } - void checkNotDeleted(const Consumer::shared_ptr& c); void notifyDeleted(); @@ -235,8 +219,9 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** * Bind self to specified exchange, and record that binding for unbinding on delete. */ - bool bind(boost::shared_ptr<Exchange> exchange, const std::string& key, - const qpid::framing::FieldTable& arguments=qpid::framing::FieldTable()); + QPID_BROKER_EXTERN bool bind( + boost::shared_ptr<Exchange> exchange, const std::string& key, + const qpid::framing::FieldTable& arguments=qpid::framing::FieldTable()); /** Acquire the message at the given position if it is available for acquire. Not to * be used by clients, but used by the broker for queue management. @@ -271,28 +256,29 @@ class Queue : public boost::enable_shared_from_this<Queue>, bool exclusive = false); QPID_BROKER_EXTERN void cancel(Consumer::shared_ptr c); - uint32_t purge(const uint32_t purge_request=0, //defaults to all messages + QPID_BROKER_EXTERN uint32_t purge(const uint32_t purge_request=0, //defaults to all messages boost::shared_ptr<Exchange> dest=boost::shared_ptr<Exchange>(), const ::qpid::types::Variant::Map *filter=0); QPID_BROKER_EXTERN void purgeExpired(sys::Duration); //move qty # of messages to destination Queue destq - uint32_t move(const Queue::shared_ptr destq, uint32_t qty, - const qpid::types::Variant::Map *filter=0); + QPID_BROKER_EXTERN uint32_t move( + const Queue::shared_ptr destq, uint32_t qty, + const qpid::types::Variant::Map *filter=0); QPID_BROKER_EXTERN uint32_t getMessageCount() const; QPID_BROKER_EXTERN uint32_t getEnqueueCompleteMessageCount() const; QPID_BROKER_EXTERN uint32_t getConsumerCount() const; inline const std::string& getName() const { return name; } - bool isExclusiveOwner(const OwnershipToken* const o) const; - void releaseExclusiveOwnership(); - bool setExclusiveOwner(const OwnershipToken* const o); - bool hasExclusiveConsumer() const; - bool hasExclusiveOwner() const; + QPID_BROKER_EXTERN bool isExclusiveOwner(const OwnershipToken* const o) const; + QPID_BROKER_EXTERN void releaseExclusiveOwnership(); + QPID_BROKER_EXTERN bool setExclusiveOwner(const OwnershipToken* const o); + QPID_BROKER_EXTERN bool hasExclusiveConsumer() const; + QPID_BROKER_EXTERN bool hasExclusiveOwner() const; inline bool isDurable() const { return store != 0; } inline const framing::FieldTable& getSettings() const { return settings; } inline bool isAutoDelete() const { return autodelete; } - bool canAutoDelete() const; + QPID_BROKER_EXTERN bool canAutoDelete() const; const QueueBindings& getBindings() const { return bindings; } /** @@ -301,8 +287,8 @@ class Queue : public boost::enable_shared_from_this<Queue>, QPID_BROKER_EXTERN void setLastNodeFailure(); QPID_BROKER_EXTERN void clearLastNodeFailure(); - bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck = false); - void enqueueAborted(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck = false); + QPID_BROKER_EXTERN void enqueueAborted(boost::intrusive_ptr<Message> msg); /** * dequeue from store (only done once messages is acknowledged) */ @@ -311,7 +297,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * Inform the queue that a previous transactional dequeue * committed. */ - void dequeueCommitted(const QueuedMessage& msg); + QPID_BROKER_EXTERN void dequeueCommitted(const QueuedMessage& msg); /** * Inform queue of messages that were enqueued, have since @@ -319,7 +305,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * thus are still logically on the queue) - used in * clustered broker. */ - void updateEnqueued(const QueuedMessage& msg); + QPID_BROKER_EXTERN void updateEnqueued(const QueuedMessage& msg); /** * Test whether the specified message (identified by its @@ -328,7 +314,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * have been delievered to a subscriber who has not yet * accepted it). */ - bool isEnqueued(const QueuedMessage& msg); + QPID_BROKER_EXTERN bool isEnqueued(const QueuedMessage& msg); /** * Acquires the next available (oldest) message @@ -338,17 +324,17 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** Get the message at position pos, returns true if found and sets msg */ QPID_BROKER_EXTERN bool find(framing::SequenceNumber pos, QueuedMessage& msg ) const; - const QueuePolicy* getPolicy(); + QPID_BROKER_EXTERN const QueuePolicy* getPolicy(); - void setAlternateExchange(boost::shared_ptr<Exchange> exchange); - boost::shared_ptr<Exchange> getAlternateExchange(); - bool isLocal(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void setAlternateExchange(boost::shared_ptr<Exchange> exchange); + QPID_BROKER_EXTERN boost::shared_ptr<Exchange> getAlternateExchange(); + QPID_BROKER_EXTERN bool isLocal(boost::intrusive_ptr<Message>& msg); //PersistableQueue support: - uint64_t getPersistenceId() const; - void setPersistenceId(uint64_t persistenceId) const; - void encode(framing::Buffer& buffer) const; - uint32_t encodedSize() const; + QPID_BROKER_EXTERN uint64_t getPersistenceId() const; + QPID_BROKER_EXTERN void setPersistenceId(uint64_t persistenceId) const; + QPID_BROKER_EXTERN void encode(framing::Buffer& buffer) const; + QPID_BROKER_EXTERN uint32_t encodedSize() const; /** * Restores a queue from encoded data (used in recovery) @@ -362,15 +348,15 @@ class Queue : public boost::enable_shared_from_this<Queue>, virtual void setExternalQueueStore(ExternalQueueStore* inst); // Increment the rejected-by-consumer counter. - void countRejected() const; - void countFlowedToDisk(uint64_t size) const; - void countLoadedFromDisk(uint64_t size) const; + QPID_BROKER_EXTERN void countRejected() const; + QPID_BROKER_EXTERN void countFlowedToDisk(uint64_t size) const; + QPID_BROKER_EXTERN void countLoadedFromDisk(uint64_t size) const; // Manageable entry points - management::ManagementObject* GetManagementObject (void) const; + QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject (void) const; management::Manageable::status_t - ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); - void query(::qpid::types::Variant::Map&) const; + QPID_BROKER_EXTERN ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + QPID_BROKER_EXTERN void query(::qpid::types::Variant::Map&) const; /** Apply f to each Message on the queue. */ template <class F> void eachMessage(F f) { @@ -385,6 +371,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** Apply f to each Observer on the queue */ template <class F> void eachObserver(F f) { + sys::Mutex::ScopedLock l(messageLock); std::for_each<Observers::iterator, F>(observers.begin(), observers.end(), f); } @@ -396,31 +383,31 @@ class Queue : public boost::enable_shared_from_this<Queue>, /** return current position sequence number for the next message on the queue. */ QPID_BROKER_EXTERN framing::SequenceNumber getPosition(); - void addObserver(boost::shared_ptr<QueueObserver>); - void removeObserver(boost::shared_ptr<QueueObserver>); + QPID_BROKER_EXTERN void addObserver(boost::shared_ptr<QueueObserver>); + QPID_BROKER_EXTERN void removeObserver(boost::shared_ptr<QueueObserver>); QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key); /** * Notify queue that recovery has completed. */ - void recoveryComplete(ExchangeRegistry& exchanges); + QPID_BROKER_EXTERN void recoveryComplete(ExchangeRegistry& exchanges); // For cluster update - QueueListeners& getListeners(); - Messages& getMessages(); - const Messages& getMessages() const; + QPID_BROKER_EXTERN QueueListeners& getListeners(); + QPID_BROKER_EXTERN Messages& getMessages(); + QPID_BROKER_EXTERN const Messages& getMessages() const; /** * Reserve space in policy for an enqueued message that * has been recovered in the prepared state (dtx only) */ - void recoverPrepared(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void recoverPrepared(boost::intrusive_ptr<Message>& msg); - void flush(); + QPID_BROKER_EXTERN void flush(); - Broker* getBroker(); + QPID_BROKER_EXTERN Broker* getBroker(); uint32_t getDequeueSincePurge() { return dequeueSincePurge.get(); } - void setDequeueSincePurge(uint32_t value); + QPID_BROKER_EXTERN void setDequeueSincePurge(uint32_t value); }; } } diff --git a/cpp/src/qpid/broker/QueueListeners.cpp b/cpp/src/qpid/broker/QueueListeners.cpp index 32c208b073..0338a674cf 100644 --- a/cpp/src/qpid/broker/QueueListeners.cpp +++ b/cpp/src/qpid/broker/QueueListeners.cpp @@ -79,10 +79,6 @@ void QueueListeners::NotificationSet::notify() std::for_each(browsers.begin(), browsers.end(), boost::mem_fn(&Consumer::notify)); } -bool QueueListeners::contains(Consumer::shared_ptr c) const { - return c->inListeners; -} - void QueueListeners::ListenerSet::notifyAll() { std::for_each(listeners.begin(), listeners.end(), boost::mem_fn(&Consumer::notify)); diff --git a/cpp/src/qpid/broker/QueueListeners.h b/cpp/src/qpid/broker/QueueListeners.h index 0659499253..ca844fd47e 100644 --- a/cpp/src/qpid/broker/QueueListeners.h +++ b/cpp/src/qpid/broker/QueueListeners.h @@ -30,7 +30,7 @@ namespace broker { /** * Track and notify components that wish to be notified of messages * that become available on a queue. - * + * * None of the methods defined here are protected by locking. However * the populate method allows a 'snapshot' to be taken of the * listeners to be notified. NotificationSet::notify() may then be @@ -61,11 +61,10 @@ class QueueListeners friend class QueueListeners; }; - void addListener(Consumer::shared_ptr); - void removeListener(Consumer::shared_ptr); + void addListener(Consumer::shared_ptr); + void removeListener(Consumer::shared_ptr); void populate(NotificationSet&); void snapshot(ListenerSet&); - bool contains(Consumer::shared_ptr c) const; void notifyAll(); template <class F> void eachListener(F f) { diff --git a/cpp/src/qpid/sys/apr/Time.cpp b/cpp/src/qpid/broker/QueuedMessage.cpp index 34e740b144..d40cc901ff 100644 --- a/cpp/src/qpid/sys/apr/Time.cpp +++ b/cpp/src/qpid/broker/QueuedMessage.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,19 +18,17 @@ * under the License. * */ - -#include "qpid/sys/Time.h" - -#include <apr_time.h> +#include "QueuedMessage.h" +#include "Queue.h" +#include <iostream> namespace qpid { -namespace sys { - -AbsTime AbsTime::now() { - AbsTime time_now; - time_now.time_ns = apr_time_now() * TIME_USEC; - return time_now; +namespace broker { + +std::ostream& operator<<(std::ostream& o, const QueuedMessage& qm) { + o << (qm.queue ? qm.queue->getName() : std::string()) << "[" << qm.position <<"]"; + return o; } -}} +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueuedMessage.h b/cpp/src/qpid/broker/QueuedMessage.h index 806da8e720..9d008193a0 100644 --- a/cpp/src/qpid/broker/QueuedMessage.h +++ b/cpp/src/qpid/broker/QueuedMessage.h @@ -22,6 +22,8 @@ #define _QueuedMessage_ #include "qpid/broker/Message.h" +#include "BrokerImportExport.h" +#include <iosfwd> namespace qpid { namespace broker { @@ -47,6 +49,7 @@ inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) { return a.position < b.position; } +QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueuedMessage&); }} diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp index d7adbd68ab..80fa5e1c0e 100644 --- a/cpp/src/qpid/broker/SaslAuthenticator.cpp +++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp @@ -26,6 +26,7 @@ #include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include "qpid/sys/SecuritySettings.h" #include <boost/format.hpp> diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp index e7d2259c80..64924bdd4c 100644 --- a/cpp/src/qpid/broker/SemanticState.cpp +++ b/cpp/src/qpid/broker/SemanticState.cpp @@ -489,14 +489,14 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { exchangeName << " with routing-key " << msg->getRoutingKey())); } - cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + cacheExchange->route(strategy); if (!strategy.delivered) { //TODO:if discard-unroutable, just drop it //TODO:else if accept-mode is explicit, reject it //else route it to alternate exchange if (cacheExchange->getAlternate()) { - cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + cacheExchange->getAlternate()->route(strategy); } if (!strategy.delivered) { msg->destroy(); diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h index 5a83fd0fb3..e5e1d2da16 100644 --- a/cpp/src/qpid/broker/SemanticState.h +++ b/cpp/src/qpid/broker/SemanticState.h @@ -22,6 +22,7 @@ * */ +#include "qpid/broker/BrokerImportExport.h" #include "qpid/broker/Consumer.h" #include "qpid/broker/Credit.h" #include "qpid/broker/Deliverable.h" @@ -39,7 +40,6 @@ #include "qpid/sys/AggregateOutput.h" #include "qpid/sys/Mutex.h" #include "qpid/sys/AtomicValue.h" -#include "qpid/broker/AclModule.h" #include "qmf/org/apache/qpid/broker/Subscription.h" #include <list> @@ -99,42 +99,44 @@ class SemanticState : private boost::noncopyable { bool haveCredit(); protected: - virtual bool doDispatch(); + QPID_BROKER_EXTERN virtual bool doDispatch(); size_t unacked() { return parent->unacked.size(); } public: typedef boost::shared_ptr<ConsumerImpl> shared_ptr; - ConsumerImpl(SemanticState* parent, - const std::string& name, boost::shared_ptr<Queue> queue, - bool ack, bool acquire, bool exclusive, - const std::string& tag, const std::string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments); - virtual ~ConsumerImpl(); - OwnershipToken* getSession(); - virtual bool deliver(QueuedMessage& msg); - bool filter(boost::intrusive_ptr<Message> msg); - bool accept(boost::intrusive_ptr<Message> msg); - void cancel() {} - - void disableNotify(); - void enableNotify(); - void notify(); - bool isNotifyEnabled() const; - - void requestDispatch(); - - void setWindowMode(); - void setCreditMode(); - void addByteCredit(uint32_t value); - void addMessageCredit(uint32_t value); - void flush(); - void stop(); - void complete(DeliveryRecord&); + QPID_BROKER_EXTERN ConsumerImpl( + SemanticState* parent, + const std::string& name, boost::shared_ptr<Queue> queue, + bool ack, bool acquire, bool exclusive, + const std::string& tag, const std::string& resumeId, uint64_t resumeTtl, + const framing::FieldTable& arguments); + QPID_BROKER_EXTERN virtual ~ConsumerImpl(); + QPID_BROKER_EXTERN OwnershipToken* getSession(); + QPID_BROKER_EXTERN virtual bool deliver(QueuedMessage& msg); + QPID_BROKER_EXTERN bool filter(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN bool accept(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN void cancel() {} + + QPID_BROKER_EXTERN void disableNotify(); + QPID_BROKER_EXTERN void enableNotify(); + QPID_BROKER_EXTERN void notify(); + QPID_BROKER_EXTERN bool isNotifyEnabled() const; + + QPID_BROKER_EXTERN void requestDispatch(); + + QPID_BROKER_EXTERN void setWindowMode(); + QPID_BROKER_EXTERN void setCreditMode(); + QPID_BROKER_EXTERN void addByteCredit(uint32_t value); + QPID_BROKER_EXTERN void addMessageCredit(uint32_t value); + QPID_BROKER_EXTERN void flush(); + QPID_BROKER_EXTERN void stop(); + QPID_BROKER_EXTERN void complete(DeliveryRecord&); boost::shared_ptr<Queue> getQueue() const { return queue; } bool isBlocked() const { return blocked; } bool setBlocked(bool set) { std::swap(set, blocked); return set; } - bool doOutput(); + QPID_BROKER_EXTERN bool doOutput(); Credit& getCredit() { return credit; } const Credit& getCredit() const { return credit; } @@ -152,8 +154,11 @@ class SemanticState : private boost::noncopyable { void acknowledged(const broker::QueuedMessage&) {} // manageable entry points - management::ManagementObject* GetManagementObject (void) const; - management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + QPID_BROKER_EXTERN management::ManagementObject* + GetManagementObject(void) const; + + QPID_BROKER_EXTERN management::Manageable::status_t + ManagementMethod(uint32_t methodId, management::Args& args, std::string& text); }; typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap; diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp index 4aad46f782..78f2e43ce0 100644 --- a/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/cpp/src/qpid/broker/SessionAdapter.cpp @@ -21,8 +21,9 @@ #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/enum.h" -#include "qpid/log/Statement.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/SequenceSet.h" +#include "qpid/log/Statement.h" #include "qpid/management/ManagementAgent.h" #include "qpid/broker/SessionState.h" #include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h" @@ -73,18 +74,12 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const if(passive){ AclModule* acl = getBroker().getAcl(); if (acl) { - //TODO: why does a passive declare require create - //permission? The purpose of the passive flag is to state - //that the exchange should *not* created. For - //authorisation a passive declare is similar to - //exchange-query. std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_TYPE, type)); params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _TRUE)); params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE)); - if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,¶ms) ) - throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << getConnection().getUserId())); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchange,¶ms) ) + throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange access request from " << getConnection().getUserId())); } Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange)); checkType(actual, type); @@ -274,22 +269,16 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& if (passive && !name.empty()) { AclModule* acl = getBroker().getAcl(); if (acl) { - //TODO: why does a passive declare require create - //permission? The purpose of the passive flag is to state - //that the queue should *not* created. For - //authorisation a passive declare is similar to - //queue-query (or indeed a qmf query). std::map<acl::Property, std::string> params; params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); - params.insert(make_pair(acl::PROP_PASSIVE, _TRUE)); params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE))); params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type"))); params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count")))); params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size")))); - if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) - throw UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId())); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,¶ms) ) + throw UnauthorizedAccessException(QPID_MSG("ACL denied queue access request from " << getConnection().getUserId())); } queue = getQueue(name); //TODO: check alternate-exchange is as expected @@ -409,6 +398,7 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, if(!destination.empty() && state.exists(destination)) throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + // We allow browsing (acquireMode == 1) of exclusive queues, this is required by HA. if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session) && acquireMode == 0) throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue " << queue->getName())); @@ -548,13 +538,6 @@ void SessionAdapter::TxHandlerImpl::rollback() state.rollback(); } -std::string SessionAdapter::DtxHandlerImpl::convert(const framing::Xid& xid) -{ - std::string encoded; - encode(xid, encoded); - return encoded; -} - void SessionAdapter::DtxHandlerImpl::select() { state.selectDtx(); @@ -566,7 +549,7 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, { try { if (fail) { - state.endDtx(convert(xid), true); + state.endDtx(DtxManager::convert(xid), true); if (suspend) { throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set.")); } else { @@ -574,9 +557,9 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, } } else { if (suspend) { - state.suspendDtx(convert(xid)); + state.suspendDtx(DtxManager::convert(xid)); } else { - state.endDtx(convert(xid), false); + state.endDtx(DtxManager::convert(xid), false); } return XaResult(XA_STATUS_XA_OK); } @@ -594,9 +577,9 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid, } try { if (resume) { - state.resumeDtx(convert(xid)); + state.resumeDtx(DtxManager::convert(xid)); } else { - state.startDtx(convert(xid), getBroker().getDtxManager(), join); + state.startDtx(DtxManager::convert(xid), getBroker().getDtxManager(), join); } return XaResult(XA_STATUS_XA_OK); } catch (const DtxTimeoutException& /*e*/) { @@ -607,7 +590,7 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid, XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid) { try { - bool ok = getBroker().getDtxManager().prepare(convert(xid)); + bool ok = getBroker().getDtxManager().prepare(DtxManager::convert(xid)); return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); } catch (const DtxTimeoutException& /*e*/) { return XaResult(XA_STATUS_XA_RBTIMEOUT); @@ -618,7 +601,7 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid, bool onePhase) { try { - bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase); + bool ok = getBroker().getDtxManager().commit(DtxManager::convert(xid), onePhase); return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); } catch (const DtxTimeoutException& /*e*/) { return XaResult(XA_STATUS_XA_RBTIMEOUT); @@ -629,7 +612,7 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid, XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid) { try { - getBroker().getDtxManager().rollback(convert(xid)); + getBroker().getDtxManager().rollback(DtxManager::convert(xid)); return XaResult(XA_STATUS_XA_OK); } catch (const DtxTimeoutException& /*e*/) { return XaResult(XA_STATUS_XA_RBTIMEOUT); @@ -659,7 +642,7 @@ void SessionAdapter::DtxHandlerImpl::forget(const Xid& xid) DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid) { - uint32_t timeout = getBroker().getDtxManager().getTimeout(convert(xid)); + uint32_t timeout = getBroker().getDtxManager().getTimeout(DtxManager::convert(xid)); return DtxGetTimeoutResult(timeout); } @@ -667,7 +650,7 @@ DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid) void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid, uint32_t timeout) { - getBroker().getDtxManager().setTimeout(convert(xid), timeout); + getBroker().getDtxManager().setTimeout(DtxManager::convert(xid), timeout); } diff --git a/cpp/src/qpid/broker/SessionAdapter.h b/cpp/src/qpid/broker/SessionAdapter.h index 8987c4812f..bc056538b1 100644 --- a/cpp/src/qpid/broker/SessionAdapter.h +++ b/cpp/src/qpid/broker/SessionAdapter.h @@ -226,10 +226,8 @@ class Queue; void rollback(); }; - class DtxHandlerImpl : public DtxHandler, public HandlerHelper, private framing::StructHelper + class DtxHandlerImpl : public DtxHandler, public HandlerHelper { - std::string convert(const framing::Xid& xid); - public: DtxHandlerImpl(SemanticState& session) : HandlerHelper(session) {} diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp index 752fa55535..b58c7c01c5 100644 --- a/cpp/src/qpid/broker/SessionHandler.cpp +++ b/cpp/src/qpid/broker/SessionHandler.cpp @@ -64,6 +64,7 @@ void SessionHandler::handleDetach() { if (session.get()) connection.getBroker().getSessionManager().detach(session); assert(!session.get()); + if (detachedCallback) detachedCallback(); connection.closeChannel(channel.get()); } @@ -117,4 +118,8 @@ void SessionHandler::attached(const std::string& name) } } +void SessionHandler::setDetachedCallback(boost::function<void()> cb) { + detachedCallback = cb; +} + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h index 8cd5072574..4e2cfaa963 100644 --- a/cpp/src/qpid/broker/SessionHandler.h +++ b/cpp/src/qpid/broker/SessionHandler.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,7 @@ #include "qpid/amqp_0_10/SessionHandler.h" #include "qpid/broker/SessionHandler.h" #include "qpid/framing/AMQP_ClientProxy.h" +#include <boost/function.hpp> namespace qpid { class SessionState; @@ -61,7 +62,7 @@ class SessionHandler : public amqp_0_10::SessionHandler { * This proxy is for sending such commands. In a clustered broker it will take steps * to synchronize command order across the cluster. In a stand-alone broker * it is just a synonym for getProxy() - */ + */ framing::AMQP_ClientProxy& getClusterOrderProxy() { return clusterOrderProxy.get() ? *clusterOrderProxy : proxy; } @@ -70,6 +71,8 @@ class SessionHandler : public amqp_0_10::SessionHandler { void attached(const std::string& name);//used by 'pushing' inter-broker bridges void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges + void setDetachedCallback(boost::function<void()> cb); + protected: virtual void setState(const std::string& sessionName, bool force); virtual qpid::SessionState* getState(); @@ -91,6 +94,7 @@ class SessionHandler : public amqp_0_10::SessionHandler { framing::AMQP_ClientProxy proxy; std::auto_ptr<SessionState> session; std::auto_ptr<SetChannelProxy> clusterOrderProxy; + boost::function<void ()> detachedCallback; }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp index 644a3d628e..dd3ec13019 100644 --- a/cpp/src/qpid/broker/TopicExchange.cpp +++ b/cpp/src/qpid/broker/TopicExchange.cpp @@ -350,8 +350,9 @@ TopicExchange::BindingKey *TopicExchange::getQueueBinding(Queue::shared_ptr queu return (q != qv.end()) ? bk : 0; } -void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +void TopicExchange::route(Deliverable& msg) { + const string& routingKey = msg.getMessage().getRoutingKey(); // Note: PERFORMANCE CRITICAL!!! BindingList b; std::map<std::string, BindingList>::iterator it; diff --git a/cpp/src/qpid/broker/TopicExchange.h b/cpp/src/qpid/broker/TopicExchange.h index 636918f8a1..cc24e1411e 100644 --- a/cpp/src/qpid/broker/TopicExchange.h +++ b/cpp/src/qpid/broker/TopicExchange.h @@ -185,9 +185,7 @@ class TopicExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - QPID_BROKER_EXTERN virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg); QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp index 2acc09cded..a38e6ac12a 100644 --- a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp +++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp @@ -25,6 +25,7 @@ #include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include <windows.h> diff --git a/cpp/src/qpid/client/Connection.cpp b/cpp/src/qpid/client/Connection.cpp index 2882ef5d42..83a4a35b53 100644 --- a/cpp/src/qpid/client/Connection.cpp +++ b/cpp/src/qpid/client/Connection.cpp @@ -75,7 +75,7 @@ void Connection::open(const Url& url, const ConnectionSettings& settings) { i++; try { ConnectionSettings cs(settings); - cs.protocol = addr.protocol; + if (addr.protocol.size()) cs.protocol = addr.protocol; cs.host = addr.host; cs.port = addr.port; open(cs); diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp index ab0d8e0700..94561f8079 100644 --- a/cpp/src/qpid/client/ConnectionHandler.cpp +++ b/cpp/src/qpid/client/ConnectionHandler.cpp @@ -28,10 +28,13 @@ #include "qpid/framing/all_method_bodies.h" #include "qpid/framing/ClientInvoker.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Helpers.h" #include "qpid/log/Statement.h" #include "qpid/sys/SystemInfo.h" +#include <algorithm> + using namespace qpid::client; using namespace qpid::framing; using namespace qpid::framing::connection; @@ -238,15 +241,16 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me ); std::vector<std::string> mechlist; + mechlist.reserve(mechanisms.size()); if (mechanism.empty()) { //mechlist is simply what the server offers - mechanisms.collect(mechlist); + std::transform(mechanisms.begin(), mechanisms.end(), std::back_inserter(mechlist), Array::get<std::string, Array::ValuePtr>); } else { //mechlist is the intersection of those indicated by user and //those supported by server, in the order listed by user std::vector<std::string> allowed = split(mechanism, " "); - std::vector<std::string> supported; - mechanisms.collect(supported); + std::vector<std::string> supported(mechanisms.size()); + std::transform(mechanisms.begin(), mechanisms.end(), std::back_inserter(supported), Array::get<std::string, Array::ValuePtr>); intersection(allowed, supported, mechlist); if (mechlist.empty()) { throw Exception(QPID_MSG("Desired mechanism(s) not valid: " << mechanism << " (supported: " << join(supported) << ")")); diff --git a/cpp/src/qpid/client/ConnectionImpl.cpp b/cpp/src/qpid/client/ConnectionImpl.cpp index db97f1e0f4..85b0e8303e 100644 --- a/cpp/src/qpid/client/ConnectionImpl.cpp +++ b/cpp/src/qpid/client/ConnectionImpl.cpp @@ -115,8 +115,10 @@ public: ioThreads(0), connections(0) { + CommonOptions common("", "", QPIDC_CONF_FILE); IOThreadOptions options(c); - options.parse(0, 0, QPIDC_CONF_FILE, true); + common.parse(0, 0, common.clientConfig, true); + options.parse(0, 0, common.clientConfig, true); maxIOThreads = (options.maxIOThreads != -1) ? options.maxIOThreads : 1; } diff --git a/cpp/src/qpid/client/LoadPlugins.cpp b/cpp/src/qpid/client/LoadPlugins.cpp index 246eb60c67..d76e1d458e 100644 --- a/cpp/src/qpid/client/LoadPlugins.cpp +++ b/cpp/src/qpid/client/LoadPlugins.cpp @@ -39,10 +39,12 @@ namespace { struct LoadtimeInitialise { LoadtimeInitialise() { + CommonOptions common("", "", QPIDC_CONF_FILE); qpid::ModuleOptions moduleOptions(QPIDC_MODULE_DIR); string defaultPath (moduleOptions.loadDir); - moduleOptions.parse (0, 0, QPIDC_CONF_FILE, true); - + common.parse(0, 0, common.clientConfig, true); + moduleOptions.parse (0, 0, common.clientConfig, true); + for (vector<string>::iterator iter = moduleOptions.load.begin(); iter != moduleOptions.load.end(); iter++) diff --git a/cpp/src/qpid/client/SessionImpl.cpp b/cpp/src/qpid/client/SessionImpl.cpp index 9ac5323a53..3f3ad617f4 100644 --- a/cpp/src/qpid/client/SessionImpl.cpp +++ b/cpp/src/qpid/client/SessionImpl.cpp @@ -324,7 +324,7 @@ struct MethodContentAdaptor : MethodContent MethodContentAdaptor(const FrameSet& f) : header(*f.getHeaders()), content(f.getContent()) {} - AMQHeaderBody getHeader() const + const AMQHeaderBody& getHeader() const { return header; } diff --git a/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp index 6b6bf884ec..ab0c5c4957 100644 --- a/cpp/src/qpid/client/SslConnector.cpp +++ b/cpp/src/qpid/client/SslConnector.cpp @@ -148,8 +148,10 @@ namespace { struct StaticInit { StaticInit() { try { + CommonOptions common("", "", QPIDC_CONF_FILE); SslOptions options; - options.parse (0, 0, QPIDC_CONF_FILE, true); + common.parse(0, 0, common.clientConfig, true); + options.parse (0, 0, common.clientConfig, true); if (options.certDbPath.empty()) { QPID_LOG(info, "SSL connector not enabled, you must set QPID_SSL_CERT_DB to enable it."); } else { diff --git a/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp index 51eacf77e8..4660a41c07 100644 --- a/cpp/src/qpid/client/TCPConnector.cpp +++ b/cpp/src/qpid/client/TCPConnector.cpp @@ -97,7 +97,7 @@ void TCPConnector::connect(const std::string& host, const std::string& port) { boost::bind(&TCPConnector::connected, this, _1), boost::bind(&TCPConnector::connectFailed, this, _3)); closed = false; - identifier = str(format("[%1%]") % socket.getFullAddress()); + connector->start(poller); } @@ -120,6 +120,8 @@ void TCPConnector::start(sys::AsynchIO* aio_) { for (int i = 0; i < 4; i++) { aio->queueReadBuffer(new Buff(maxFrameSize)); } + + identifier = str(format("[%1%]") % socket.getFullAddress()); } void TCPConnector::initAmqp() { @@ -129,7 +131,7 @@ void TCPConnector::initAmqp() { void TCPConnector::connectFailed(const std::string& msg) { connector = 0; - QPID_LOG(warning, "Connect failed: " << msg << " " << identifier); + QPID_LOG(warning, "Connect failed: " << msg); socket.close(); if (!closed) closed = true; @@ -183,7 +185,7 @@ sys::ShutdownHandler* TCPConnector::getShutdownHandler() const { return shutdownHandler; } -const std::string& TCPConnector::getIdentifier() const { +const std::string& TCPConnector::getIdentifier() const { return identifier; } diff --git a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp index 5924e30dd8..a8f4fb5237 100644 --- a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp +++ b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -32,6 +32,7 @@ #include "qpid/framing/ExchangeBoundResult.h" #include "qpid/framing/ExchangeQueryResult.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/QueueQueryResult.h" #include "qpid/framing/ReplyTo.h" #include "qpid/framing/reply_exceptions.h" diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp index 3cfd2e37f2..2ea4dc0c61 100644 --- a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp +++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -41,6 +41,7 @@ using qpid::framing::Uuid; namespace { +const std::string TCP("tcp"); double FOREVER(std::numeric_limits<double>::max()); // Time values in seconds can be specified as integer or floating point values. @@ -290,7 +291,7 @@ bool ConnectionImpl::tryConnect() for (std::vector<std::string>::const_iterator i = urls.begin(); i != urls.end(); ++i) { try { QPID_LOG(info, "Trying to connect to " << *i << "..."); - Url url(*i); + Url url(*i, settings.protocol.size() ? settings.protocol : TCP); if (url.getUser().size()) settings.username = url.getUser(); if (url.getPass().size()) settings.password = url.getPass(); connection.open(url, settings); diff --git a/cpp/src/qpid/cluster/Cluster.cpp b/cpp/src/qpid/cluster/Cluster.cpp index 3c1d23c842..34aaf3d341 100644 --- a/cpp/src/qpid/cluster/Cluster.cpp +++ b/cpp/src/qpid/cluster/Cluster.cpp @@ -131,6 +131,7 @@ #include "qpid/cluster/UpdateExchange.h" #include "qpid/cluster/ClusterTimer.h" #include "qpid/cluster/CredentialsExchange.h" +#include "qpid/cluster/UpdateClient.h" #include "qpid/assert.h" #include "qmf/org/apache/qpid/cluster/ArgsClusterStopClusterNode.h" @@ -202,7 +203,7 @@ namespace arg=client::arg; * Currently use SVN revision to avoid clashes with versions from * different branches. */ -const uint32_t Cluster::CLUSTER_VERSION = 1207877; +const uint32_t Cluster::CLUSTER_VERSION = 1332342; struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler { qpid::cluster::Cluster& cluster; @@ -269,7 +270,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : "Error delivering frames", poller), failoverExchange(new FailoverExchange(broker.GetVhostObject(), &broker)), - updateDataExchange(new UpdateDataExchange(*this)), credentialsExchange(new CredentialsExchange(*this)), quorum(boost::bind(&Cluster::leave, this)), decoder(boost::bind(&Cluster::deliverFrame, this, _1)), @@ -295,15 +295,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) : // Failover exchange provides membership updates to clients. broker.getExchanges().registerExchange(failoverExchange); - // Update exchange is used during updates to replicate messages - // without modifying delivery-properties.exchange. - broker.getExchanges().registerExchange( - boost::shared_ptr<broker::Exchange>(new UpdateExchange(this))); - - // Update-data exchange is used for passing data that may be too large - // for single control frame. - broker.getExchanges().registerExchange(updateDataExchange); - // CredentialsExchange is used to authenticate new cluster members broker.getExchanges().registerExchange(credentialsExchange); @@ -680,6 +671,17 @@ void Cluster::initMapCompleted(Lock& l) { authenticate(); broker.setRecovery(false); // Ditch my current store. broker.setClusterUpdatee(true); + + // Update exchange is used during updates to replicate messages + // without modifying delivery-properties.exchange. + broker.getExchanges().registerExchange( + boost::shared_ptr<broker::Exchange>(new UpdateExchange(this))); + + // Update-data exchange is used during update for passing data that + // may be too large for single control frame. + updateDataExchange.reset(new UpdateDataExchange(*this)); + broker.getExchanges().registerExchange(updateDataExchange); + if (mAgent) mAgent->suppress(true); // Suppress mgmt output during update. state = JOINER; mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self); @@ -999,6 +1001,10 @@ void Cluster::checkUpdateIn(Lock& l) { boost::ref(broker.getExchanges()))); enableClusterSafe(); // Enable cluster-safe assertions deliverEventQueue.start(); + // FIXME aconway 2012-04-04: unregister/delete Update[Data]Exchange + updateDataExchange.reset(); + broker.getExchanges().destroy(UpdateDataExchange::EXCHANGE_NAME); + broker.getExchanges().destroy(UpdateClient::UPDATE); } else if (updateRetracted) { // Update was retracted, request another update updateRetracted = false; diff --git a/cpp/src/qpid/cluster/ClusterMap.cpp b/cpp/src/qpid/cluster/ClusterMap.cpp index a8389095c9..d9817db35f 100644 --- a/cpp/src/qpid/cluster/ClusterMap.cpp +++ b/cpp/src/qpid/cluster/ClusterMap.cpp @@ -21,6 +21,7 @@ #include "qpid/cluster/ClusterMap.h" #include "qpid/Url.h" #include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include <boost/bind.hpp> #include <algorithm> @@ -29,7 +30,8 @@ #include <ostream> using namespace std; -using namespace boost; +using boost::ref; +using boost::optional; namespace qpid { using namespace framing; diff --git a/cpp/src/qpid/cluster/ClusterTimer.cpp b/cpp/src/qpid/cluster/ClusterTimer.cpp index b4f7d00f38..90e4fa9d4d 100644 --- a/cpp/src/qpid/cluster/ClusterTimer.cpp +++ b/cpp/src/qpid/cluster/ClusterTimer.cpp @@ -24,6 +24,7 @@ #include "qpid/log/Statement.h" #include "qpid/framing/ClusterTimerWakeupBody.h" #include "qpid/framing/ClusterTimerDropBody.h" +#include "qpid/sys/ClusterSafe.h" namespace qpid { namespace cluster { @@ -107,6 +108,7 @@ void ClusterTimer::drop(intrusive_ptr<TimerTask> t) { // Deliver thread void ClusterTimer::deliverWakeup(const std::string& name) { QPID_LOG(trace, "Cluster timer wakeup delivered for " << name); + qpid::sys::assertClusterSafe(); Map::iterator i = map.find(name); if (i == map.end()) throw Exception(QPID_MSG("Cluster timer wakeup non-existent task " << name)); diff --git a/cpp/src/qpid/cluster/Connection.cpp b/cpp/src/qpid/cluster/Connection.cpp index fc6ada096f..512e0f03cb 100644 --- a/cpp/src/qpid/cluster/Connection.cpp +++ b/cpp/src/qpid/cluster/Connection.cpp @@ -47,6 +47,7 @@ #include "qpid/framing/ClusterConnectionAnnounceBody.h" #include "qpid/framing/ConnectionCloseBody.h" #include "qpid/framing/ConnectionCloseOkBody.h" +#include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include "qpid/sys/ClusterSafe.h" #include "qpid/types/Variant.h" @@ -796,6 +797,54 @@ void Connection::config(const std::string& encoded) { else throw Exception(QPID_MSG("Update failed, invalid kind of config: " << kind)); } +namespace { + // find a Link that matches the given Address + class LinkFinder { + qpid::Address id; + boost::shared_ptr<broker::Link> link; + public: + LinkFinder(const qpid::Address& _id) : id(_id) {} + boost::shared_ptr<broker::Link> getLink() { return link; } + void operator() (boost::shared_ptr<broker::Link> l) + { + if (!link) { + qpid::Address addr(l->getTransport(), l->getHost(), l->getPort()); + if (id == addr) { + link = l; + } + } + } + }; +} + +void Connection::internalState(const std::string& type, + const std::string& name, + const framing::FieldTable& state) +{ + if (type == "link") { + // name is the string representation of the Link's _configured_ destination address + Url dest; + try { + dest = name; + } catch(...) { + throw Exception(QPID_MSG("Update failed, invalid format for Link destination address: " << name)); + } + assert(dest.size()); + LinkFinder finder(dest[0]); + cluster.getBroker().getLinks().eachLink(boost::ref(finder)); + if (finder.getLink()) { + try { + finder.getLink()->setState(state); + } catch(...) { + throw Exception(QPID_MSG("Update failed, invalid state for Link " << name << ", state: " << state)); + } + QPID_LOG(debug, cluster << " updated link " << dest[0] << " with state: " << state); + } else throw Exception(QPID_MSG("Update failed, unable to find Link named: " << name)); + } + else throw Exception(QPID_MSG("Update failed, invalid object type for internal state replication: " << type)); +} + + void Connection::doCatchupIoCallbacks() { // We need to process IO callbacks during the catch-up phase in // order to service asynchronous completions for messages diff --git a/cpp/src/qpid/cluster/Connection.h b/cpp/src/qpid/cluster/Connection.h index 920c4937db..26514c76e2 100644 --- a/cpp/src/qpid/cluster/Connection.h +++ b/cpp/src/qpid/cluster/Connection.h @@ -200,6 +200,8 @@ class Connection : const std::string& instance); void config(const std::string& encoded); + void internalState(const std::string& type, const std::string& name, + const framing::FieldTable& state); void setSecureConnection ( broker::SecureConnection * sc ); diff --git a/cpp/src/qpid/cluster/CredentialsExchange.cpp b/cpp/src/qpid/cluster/CredentialsExchange.cpp index 0fafc521cd..416a3636e9 100644 --- a/cpp/src/qpid/cluster/CredentialsExchange.cpp +++ b/cpp/src/qpid/cluster/CredentialsExchange.cpp @@ -62,7 +62,8 @@ bool CredentialsExchange::check(MemberId member) { return valid; } -void CredentialsExchange::route(broker::Deliverable& msg, const string& /*routingKey*/, const framing::FieldTable* args) { +void CredentialsExchange::route(broker::Deliverable& msg) { + const framing::FieldTable* args = msg.getMessage().getApplicationHeaders(); sys::Mutex::ScopedLock l(lock); const broker::ConnectionState* connection = static_cast<const broker::ConnectionState*>(msg.getMessage().getPublisher()); diff --git a/cpp/src/qpid/cluster/CredentialsExchange.h b/cpp/src/qpid/cluster/CredentialsExchange.h index 90fd188271..74cf8350a6 100644 --- a/cpp/src/qpid/cluster/CredentialsExchange.h +++ b/cpp/src/qpid/cluster/CredentialsExchange.h @@ -50,7 +50,7 @@ class CredentialsExchange : public broker::Exchange bool check(MemberId member); /** Throw an exception if the calling connection is not the cluster user. Store credentials in msg. */ - void route(broker::Deliverable& msg, const std::string& routingKey, const framing::FieldTable* args); + void route(broker::Deliverable& msg); // Exchange overrides std::string getType() const; diff --git a/cpp/src/qpid/cluster/FailoverExchange.cpp b/cpp/src/qpid/cluster/FailoverExchange.cpp index 43ec27cf2c..87202a887c 100644 --- a/cpp/src/qpid/cluster/FailoverExchange.cpp +++ b/cpp/src/qpid/cluster/FailoverExchange.cpp @@ -80,7 +80,7 @@ bool FailoverExchange::isBound(Queue::shared_ptr queue, const string* const, con return queues.find(queue) != queues.end(); } -void FailoverExchange::route(Deliverable&, const string& , const framing::FieldTable* ) { +void FailoverExchange::route(Deliverable&) { QPID_LOG(warning, "Message received by exchange " << typeName << " ignoring"); } diff --git a/cpp/src/qpid/cluster/FailoverExchange.h b/cpp/src/qpid/cluster/FailoverExchange.h index c3e50c6929..5ac734a7ac 100644 --- a/cpp/src/qpid/cluster/FailoverExchange.h +++ b/cpp/src/qpid/cluster/FailoverExchange.h @@ -54,7 +54,7 @@ class FailoverExchange : public broker::Exchange bool bind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const framing::FieldTable* args); bool unbind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const framing::FieldTable* args); bool isBound(boost::shared_ptr<broker::Queue> queue, const std::string* const routingKey, const framing::FieldTable* const args); - void route(broker::Deliverable& msg, const std::string& routingKey, const framing::FieldTable* args); + void route(broker::Deliverable& msg); private: void sendUpdate(const boost::shared_ptr<broker::Queue>&); diff --git a/cpp/src/qpid/cluster/InitialStatusMap.cpp b/cpp/src/qpid/cluster/InitialStatusMap.cpp index eb65005a9e..fc53d1076b 100644 --- a/cpp/src/qpid/cluster/InitialStatusMap.cpp +++ b/cpp/src/qpid/cluster/InitialStatusMap.cpp @@ -30,9 +30,9 @@ namespace qpid { namespace cluster { using namespace std; -using namespace boost; using namespace framing::cluster; using namespace framing; +using boost::optional; InitialStatusMap::InitialStatusMap(const MemberId& self_, size_t size_) : self(self_), completed(), resendNeeded(), size(size_) diff --git a/cpp/src/qpid/cluster/UpdateClient.cpp b/cpp/src/qpid/cluster/UpdateClient.cpp index 95c64ff060..20684fd8a7 100644 --- a/cpp/src/qpid/cluster/UpdateClient.cpp +++ b/cpp/src/qpid/cluster/UpdateClient.cpp @@ -57,6 +57,7 @@ #include "qpid/framing/ClusterConnectionShadowReadyBody.h" #include "qpid/framing/ClusterConnectionSessionStateBody.h" #include "qpid/framing/ClusterConnectionConsumerStateBody.h" +#include "qpid/framing/FieldValue.h" #include "qpid/framing/enum.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/framing/TypeCode.h" @@ -687,7 +688,15 @@ void UpdateClient::updateLinks() { void UpdateClient::updateLink(const boost::shared_ptr<broker::Link>& link) { QPID_LOG(debug, *this << " updating link " << link->getHost() << ":" << link->getPort()); - ClusterConnectionProxy(session).config(encode(*link)); + ClusterConnectionProxy(session).config(encode(*link)); // push the configuration + // now push the current state + framing::FieldTable state; + link->getState(state); + std::ostringstream os; + os << qpid::Address(link->getTransport(), link->getHost(), link->getPort()); + ClusterConnectionProxy(session).internalState(std::string("link"), + os.str(), + state); } void UpdateClient::updateBridge(const boost::shared_ptr<broker::Bridge>& bridge) { diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.cpp b/cpp/src/qpid/cluster/UpdateDataExchange.cpp index e5cd82e3d3..31d96c67ca 100644 --- a/cpp/src/qpid/cluster/UpdateDataExchange.cpp +++ b/cpp/src/qpid/cluster/UpdateDataExchange.cpp @@ -40,9 +40,9 @@ UpdateDataExchange::UpdateDataExchange(Cluster& cluster) : Exchange(EXCHANGE_NAME, &cluster) {} -void UpdateDataExchange::route(broker::Deliverable& msg, const std::string& routingKey, - const qpid::framing::FieldTable* ) +void UpdateDataExchange::route(broker::Deliverable& msg) { + const std::string& routingKey = msg.getMessage().getRoutingKey(); std::string data = msg.getMessage().getFrames().getContent(); if (routingKey == MANAGEMENT_AGENTS_KEY) managementAgents = data; else if (routingKey == MANAGEMENT_SCHEMAS_KEY) managementSchemas = data; diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.h b/cpp/src/qpid/cluster/UpdateDataExchange.h index d2f6c35ad0..f79430f111 100644 --- a/cpp/src/qpid/cluster/UpdateDataExchange.h +++ b/cpp/src/qpid/cluster/UpdateDataExchange.h @@ -50,8 +50,7 @@ class UpdateDataExchange : public broker::Exchange UpdateDataExchange(Cluster& parent); - void route(broker::Deliverable& msg, const std::string& routingKey, - const framing::FieldTable* args); + void route(broker::Deliverable& msg); // Not implemented std::string getType() const { return EXCHANGE_TYPE; } diff --git a/cpp/src/qpid/framing/AMQFrame.h b/cpp/src/qpid/framing/AMQFrame.h index 4f6faf4199..19675ce6ff 100644 --- a/cpp/src/qpid/framing/AMQFrame.h +++ b/cpp/src/qpid/framing/AMQFrame.h @@ -43,8 +43,7 @@ class QPID_COMMON_CLASS_EXTERN AMQFrame : public AMQDataBlock ChannelId getChannel() const { return channel; } void setChannel(ChannelId c) { channel = c; } - AMQBody* getBody() { return body.get(); } - const AMQBody* getBody() const { return body.get(); } + AMQBody* getBody() const { return body.get(); } AMQMethodBody* getMethod() { return getBody() ? getBody()->getMethod() : 0; } const AMQMethodBody* getMethod() const { return getBody() ? getBody()->getMethod() : 0; } diff --git a/cpp/src/qpid/framing/BodyHandler.cpp b/cpp/src/qpid/framing/BodyHandler.cpp deleted file mode 100644 index db302b1e4c..0000000000 --- a/cpp/src/qpid/framing/BodyHandler.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/framing/BodyHandler.h" -#include "qpid/framing/AMQMethodBody.h" -#include "qpid/framing/AMQHeaderBody.h" -#include "qpid/framing/AMQContentBody.h" -#include "qpid/framing/AMQHeartbeatBody.h" -#include <boost/cast.hpp> -#include "qpid/framing/reply_exceptions.h" -#include "qpid/Msg.h" - -using namespace qpid::framing; -using namespace boost; - -BodyHandler::~BodyHandler() {} - -// TODO aconway 2007-08-13: Replace with visitor. -void BodyHandler::handleBody(AMQBody* body) { - switch(body->type()) - { - case METHOD_BODY: - handleMethod(polymorphic_downcast<AMQMethodBody*>(body)); - break; - case HEADER_BODY: - handleHeader(polymorphic_downcast<AMQHeaderBody*>(body)); - break; - case CONTENT_BODY: - handleContent(polymorphic_downcast<AMQContentBody*>(body)); - break; - case HEARTBEAT_BODY: - handleHeartbeat(polymorphic_downcast<AMQHeartbeatBody*>(body)); - break; - default: - throw FramingErrorException( - QPID_MSG("Invalid frame type " << body->type())); - } -} - diff --git a/cpp/src/qpid/framing/FieldTable.cpp b/cpp/src/qpid/framing/FieldTable.cpp index f80d2f9fb1..0f7140b627 100644 --- a/cpp/src/qpid/framing/FieldTable.cpp +++ b/cpp/src/qpid/framing/FieldTable.cpp @@ -28,29 +28,93 @@ #include "qpid/Msg.h" #include <assert.h> +// The locking rationale in the FieldTable seems a little odd, but it +// maintains the concurrent guarantees and requirements that were in +// place before the cachedBytes/cachedSize were added: +// +// The FieldTable client code needs to make sure that they call no write +// operation in parallel with any other operation on the FieldTable. +// However multiple parallel read operations are safe. +// +// To this end the only code that is locked is code that can transparently +// change the state of the FieldTable during a read only operation. +// (In other words the code that required the mutable members in the class +// definition!) +// namespace qpid { + +using sys::Mutex; +using sys::ScopedLock; + namespace framing { +FieldTable::FieldTable() : + cachedSize(0), + newBytes(false) +{ +} + FieldTable::FieldTable(const FieldTable& ft) { - *this = ft; + ScopedLock<Mutex> l(ft.lock); // lock _source_ FieldTable + + cachedBytes = ft.cachedBytes; + cachedSize = ft.cachedSize; + newBytes = ft.newBytes; + + // Only copy the values if we have no raw data + // - copying the map is expensive and we can + // reconstruct it if necessary from the raw data + if (cachedBytes) { + newBytes = true; + return; + } + // In practice Encoding the source field table and only copying + // the encoded bytes is faster than copying the whole value map. + // (Because we nearly always copy a field table internally before + // encoding it to send, but don't change it after the copy) + if (!ft.values.empty()) { + // Side effect of getting encoded size will cache it in ft.cachedSize + ft.cachedBytes = boost::shared_array<uint8_t>(new uint8_t[ft.encodedSize()]); + + Buffer buffer((char*)&ft.cachedBytes[0], ft.cachedSize); + + // Cut and paste ahead... + buffer.putLong(ft.encodedSize() - 4); + buffer.putLong(ft.values.size()); + for (ValueMap::const_iterator i = ft.values.begin(); i!=ft.values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } + + cachedBytes = ft.cachedBytes; + cachedSize = ft.cachedSize; + newBytes = true; + } } FieldTable& FieldTable::operator=(const FieldTable& ft) { - clear(); - values = ft.values; - return *this; + FieldTable nft(ft); + values.swap(nft.values); + cachedBytes.swap(nft.cachedBytes); + cachedSize = nft.cachedSize; + newBytes = nft.newBytes; + return (*this); } -FieldTable::~FieldTable() {} - uint32_t FieldTable::encodedSize() const { + ScopedLock<Mutex> l(lock); + + if (cachedSize != 0) { + return cachedSize; + } uint32_t len(4/*size field*/ + 4/*count field*/); for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { // shortstr_len_byte + key size + value size - len += 1 + (i->first).size() + (i->second)->encodedSize(); + len += 1 + (i->first).size() + (i->second)->encodedSize(); } + cachedSize = len; return len; } @@ -66,6 +130,7 @@ std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_ty } std::ostream& operator<<(std::ostream& out, const FieldTable& t) { + t.realDecode(); out << "{"; FieldTable::ValueMap::const_iterator i = t.begin(); if (i != t.end()) out << *i++; @@ -77,48 +142,70 @@ std::ostream& operator<<(std::ostream& out, const FieldTable& t) { } void FieldTable::set(const std::string& name, const ValuePtr& value){ + realDecode(); values[name] = value; + flushRawCache(); } void FieldTable::setString(const std::string& name, const std::string& value){ + realDecode(); values[name] = ValuePtr(new Str16Value(value)); + flushRawCache(); } void FieldTable::setInt(const std::string& name, const int value){ + realDecode(); values[name] = ValuePtr(new IntegerValue(value)); + flushRawCache(); } void FieldTable::setInt64(const std::string& name, const int64_t value){ + realDecode(); values[name] = ValuePtr(new Integer64Value(value)); + flushRawCache(); } void FieldTable::setTimestamp(const std::string& name, const uint64_t value){ + realDecode(); values[name] = ValuePtr(new TimeValue(value)); + flushRawCache(); } void FieldTable::setUInt64(const std::string& name, const uint64_t value){ + realDecode(); values[name] = ValuePtr(new Unsigned64Value(value)); + flushRawCache(); } void FieldTable::setTable(const std::string& name, const FieldTable& value) { + realDecode(); values[name] = ValuePtr(new FieldTableValue(value)); + flushRawCache(); } void FieldTable::setArray(const std::string& name, const Array& value) { + realDecode(); values[name] = ValuePtr(new ArrayValue(value)); + flushRawCache(); } void FieldTable::setFloat(const std::string& name, const float value){ + realDecode(); values[name] = ValuePtr(new FloatValue(value)); + flushRawCache(); } void FieldTable::setDouble(const std::string& name, double value){ + realDecode(); values[name] = ValuePtr(new DoubleValue(value)); + flushRawCache(); } FieldTable::ValuePtr FieldTable::get(const std::string& name) const { + // Ensure we have any values we're trying to read + realDecode(); ValuePtr value; ValueMap::const_iterator i = values.find(name); if ( i!=values.end() ) @@ -188,37 +275,82 @@ bool FieldTable::getDouble(const std::string& name, double& value) const { //} void FieldTable::encode(Buffer& buffer) const { - buffer.putLong(encodedSize() - 4); - buffer.putLong(values.size()); - for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { - buffer.putShortString(i->first); - i->second->encode(buffer); + // If we've still got the input field table + // we can just copy it directly to the output + if (cachedBytes) { + ScopedLock<Mutex> l(lock); + buffer.putRawData(&cachedBytes[0], cachedSize); + } else { + buffer.putLong(encodedSize() - 4); + buffer.putLong(values.size()); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } } } +// Decode lazily - just record the raw bytes until we need them void FieldTable::decode(Buffer& buffer){ - clear(); if (buffer.available() < 4) throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + uint32_t p = buffer.getPosition(); uint32_t len = buffer.getLong(); if (len) { uint32_t available = buffer.available(); if ((available < len) || (available < 4)) throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + } + ScopedLock<Mutex> l(lock); + // Throw away previous stored values + values.clear(); + // Copy data into our buffer + cachedBytes = boost::shared_array<uint8_t>(new uint8_t[len + 4]); + cachedSize = len + 4; + newBytes = true; + buffer.setPosition(p); + buffer.getRawData(&cachedBytes[0], cachedSize); +} + +void FieldTable::realDecode() const +{ + ScopedLock<Mutex> l(lock); + + // If we've got no raw data stored up then nothing to do + if (!newBytes) + return; + + Buffer buffer((char*)&cachedBytes[0], cachedSize); + uint32_t len = buffer.getLong(); + if (len) { + uint32_t available = buffer.available(); uint32_t count = buffer.getLong(); uint32_t leftover = available - len; while(buffer.available() > leftover && count--){ std::string name; ValuePtr value(new FieldValue); - + buffer.getShortString(name); value->decode(buffer); values[name] = ValuePtr(value); - } + } } + newBytes = false; +} + +void FieldTable::flushRawCache() +{ + ScopedLock<Mutex> l(lock); + // We can only flush the cache if there are no cached bytes to decode + assert(newBytes==false); + // Avoid recreating shared array unless we actually have one. + if (cachedBytes) cachedBytes.reset(); + cachedSize = 0; } bool FieldTable::operator==(const FieldTable& x) const { + realDecode(); + x.realDecode(); if (values.size() != x.values.size()) return false; for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { ValueMap::const_iterator j = x.values.find(i->first); @@ -230,20 +362,73 @@ bool FieldTable::operator==(const FieldTable& x) const { void FieldTable::erase(const std::string& name) { - if (values.find(name) != values.end()) + realDecode(); + if (values.find(name) != values.end()) { values.erase(name); + flushRawCache(); + } +} + +void FieldTable::clear() +{ + values.clear(); + newBytes = false; + flushRawCache(); +} + +// Map-like interface. +FieldTable::ValueMap::const_iterator FieldTable::begin() const +{ + realDecode(); + return values.begin(); +} + +FieldTable::ValueMap::const_iterator FieldTable::end() const +{ + realDecode(); + return values.end(); +} + +FieldTable::ValueMap::const_iterator FieldTable::find(const std::string& s) const +{ + realDecode(); + return values.find(s); +} + +FieldTable::ValueMap::iterator FieldTable::begin() +{ + realDecode(); + flushRawCache(); + return values.begin(); +} + +FieldTable::ValueMap::iterator FieldTable::end() +{ + realDecode(); + flushRawCache(); + return values.end(); +} + +FieldTable::ValueMap::iterator FieldTable::find(const std::string& s) +{ + realDecode(); + flushRawCache(); + return values.find(s); } std::pair<FieldTable::ValueMap::iterator, bool> FieldTable::insert(const ValueMap::value_type& value) { + realDecode(); + flushRawCache(); return values.insert(value); } FieldTable::ValueMap::iterator FieldTable::insert(ValueMap::iterator position, const ValueMap::value_type& value) { + realDecode(); + flushRawCache(); return values.insert(position, value); } - } } diff --git a/cpp/src/qpid/framing/FrameSet.cpp b/cpp/src/qpid/framing/FrameSet.cpp index 255aaf6e6b..9aee7b98b9 100644 --- a/cpp/src/qpid/framing/FrameSet.cpp +++ b/cpp/src/qpid/framing/FrameSet.cpp @@ -26,7 +26,6 @@ #include "qpid/framing/TypeFilter.h" using namespace qpid::framing; -using namespace boost; FrameSet::FrameSet(const SequenceNumber& _id) : id(_id),contentSize(0),recalculateSize(true) { } FrameSet::FrameSet(const FrameSet& original) : id(original.id), contentSize(0), recalculateSize(true) @@ -103,3 +102,7 @@ std::string FrameSet::getContent() const { getContent(out); return out; } + +bool FrameSet::hasContent() const { + return parts.size() >= 3; +} diff --git a/cpp/src/qpid/framing/FrameSet.h b/cpp/src/qpid/framing/FrameSet.h index cae75e5ec8..3b9f60950b 100644 --- a/cpp/src/qpid/framing/FrameSet.h +++ b/cpp/src/qpid/framing/FrameSet.h @@ -54,6 +54,7 @@ public: QPID_COMMON_EXTERN void getContent(std::string&) const; QPID_COMMON_EXTERN std::string getContent() const; + QPID_COMMON_EXTERN bool hasContent() const; bool isContentBearing() const; diff --git a/cpp/src/qpid/framing/MethodContent.h b/cpp/src/qpid/framing/MethodContent.h index b290a0c140..58c9143cfa 100644 --- a/cpp/src/qpid/framing/MethodContent.h +++ b/cpp/src/qpid/framing/MethodContent.h @@ -32,7 +32,7 @@ class MethodContent public: virtual ~MethodContent() {} //TODO: rethink this interface - virtual AMQHeaderBody getHeader() const = 0; + virtual const AMQHeaderBody& getHeader() const = 0; virtual const std::string& getData() const = 0; }; diff --git a/cpp/src/qpid/framing/TransferContent.cpp b/cpp/src/qpid/framing/TransferContent.cpp index 837d7d346a..d997b24304 100644 --- a/cpp/src/qpid/framing/TransferContent.cpp +++ b/cpp/src/qpid/framing/TransferContent.cpp @@ -30,7 +30,7 @@ TransferContent::TransferContent(const std::string& data, const std::string& key } -AMQHeaderBody TransferContent::getHeader() const +const AMQHeaderBody& TransferContent::getHeader() const { return header; } diff --git a/cpp/src/qpid/framing/TransferContent.h b/cpp/src/qpid/framing/TransferContent.h index 9a698a1823..32663d7020 100644 --- a/cpp/src/qpid/framing/TransferContent.h +++ b/cpp/src/qpid/framing/TransferContent.h @@ -40,7 +40,7 @@ public: QPID_COMMON_EXTERN TransferContent(const std::string& data = std::string(), const std::string& key=std::string()); ///@internal - QPID_COMMON_EXTERN AMQHeaderBody getHeader() const; + QPID_COMMON_EXTERN const AMQHeaderBody& getHeader() const; QPID_COMMON_EXTERN void setData(const std::string&); QPID_COMMON_EXTERN const std::string& getData() const; diff --git a/cpp/src/qpid/framing/amqp_framing.h b/cpp/src/qpid/framing/amqp_framing.h index 3a8b39afb5..2e58922364 100644 --- a/cpp/src/qpid/framing/amqp_framing.h +++ b/cpp/src/qpid/framing/amqp_framing.h @@ -21,7 +21,6 @@ #include "qpid/framing/amqp_types.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/AMQBody.h" -#include "qpid/framing/BodyHandler.h" #include "qpid/framing/AMQMethodBody.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/framing/AMQContentBody.h" diff --git a/cpp/src/qpid/ha/Backup.cpp b/cpp/src/qpid/ha/Backup.cpp index 5acbfb9d5f..3f3fa87a01 100644 --- a/cpp/src/qpid/ha/Backup.cpp +++ b/cpp/src/qpid/ha/Backup.cpp @@ -19,10 +19,11 @@ * */ #include "Backup.h" -#include "Settings.h" #include "BrokerReplicator.h" -#include "ReplicatingSubscription.h" #include "ConnectionExcluder.h" +#include "HaBroker.h" +#include "ReplicatingSubscription.h" +#include "Settings.h" #include "qpid/Url.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/broker/Bridge.h" @@ -43,37 +44,44 @@ using namespace broker; using types::Variant; using std::string; -Backup::Backup(broker::Broker& b, const Settings& s) : - broker(b), settings(s), excluder(new ConnectionExcluder()) +Backup::Backup(HaBroker& hb, const Settings& s) : + haBroker(hb), broker(hb.getBroker()), settings(s), excluder(new ConnectionExcluder()) { + // Exclude client connections before starting the link to avoid self-connection. + broker.getConnectionObservers().add(excluder); // Empty brokerUrl means delay initialization until setUrl() is called. if (!s.brokerUrl.empty()) initialize(Url(s.brokerUrl)); } void Backup::initialize(const Url& url) { - assert(!url.empty()); - QPID_LOG(notice, "Ha: Backup started: " << url); + if (url.empty()) throw Url::Invalid("HA broker URL is empty"); + QPID_LOG(notice, "HA: Backup initialized: " << url); string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol; // Declare the link std::pair<Link::shared_ptr, bool> result = broker.getLinks().declare( url[0].host, url[0].port, protocol, false, // durable settings.mechanism, settings.username, settings.password); - assert(result.second); // FIXME aconway 2011-11-23: error handling link = result.first; link->setUrl(url); - - replicator.reset(new BrokerReplicator(link)); + replicator.reset(new BrokerReplicator(haBroker, link)); broker.getExchanges().registerExchange(replicator); - broker.getConnectionObservers().add(excluder); } +Backup::~Backup() { + if (link) link->close(); + if (replicator.get()) broker.getExchanges().destroy(replicator->getName()); + replicator.reset(); + broker.getConnectionObservers().remove(excluder); // This allows client connections. +} + + void Backup::setBrokerUrl(const Url& url) { // Ignore empty URLs seen during start-up for some tests. if (url.empty()) return; sys::Mutex::ScopedLock l(lock); if (link) { // URL changed after we initialized. - QPID_LOG(info, "HA: Backup failover URL set to " << url); + QPID_LOG(info, "HA: Backup broker URL set to " << url); link->setUrl(url); } else { @@ -81,10 +89,4 @@ void Backup::setBrokerUrl(const Url& url) { } } -Backup::~Backup() { - if (link) link->close(); - if (replicator.get()) broker.getExchanges().destroy(replicator->getName()); - broker.getConnectionObservers().remove(excluder); // This allows client connections. -} - }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/Backup.h b/cpp/src/qpid/ha/Backup.h index 526b238b82..6c36996914 100644 --- a/cpp/src/qpid/ha/Backup.h +++ b/cpp/src/qpid/ha/Backup.h @@ -38,6 +38,7 @@ namespace ha { class Settings; class ConnectionExcluder; class BrokerReplicator; +class HaBroker; /** * State associated with a backup broker. Manages connections to primary. @@ -47,7 +48,7 @@ class BrokerReplicator; class Backup { public: - Backup(broker::Broker&, const Settings&); + Backup(HaBroker&, const Settings&); ~Backup(); void setBrokerUrl(const Url&); @@ -55,6 +56,7 @@ class Backup void initialize(const Url&); sys::Mutex lock; + HaBroker& haBroker; broker::Broker& broker; Settings settings; boost::shared_ptr<broker::Link> link; diff --git a/cpp/src/qpid/ha/BrokerReplicator.cpp b/cpp/src/qpid/ha/BrokerReplicator.cpp index a8f05c1fe3..d0c99cbdb6 100644 --- a/cpp/src/qpid/ha/BrokerReplicator.cpp +++ b/cpp/src/qpid/ha/BrokerReplicator.cpp @@ -19,6 +19,7 @@ * */ #include "BrokerReplicator.h" +#include "HaBroker.h" #include "QueueReplicator.h" #include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" @@ -37,6 +38,7 @@ #include "qmf/org/apache/qpid/broker/EventQueueDelete.h" #include "qmf/org/apache/qpid/broker/EventSubscribe.h" #include <algorithm> +#include <sstream> namespace qpid { namespace ha { @@ -87,6 +89,7 @@ const string QUEUE("queue"); const string RHOST("rhost"); const string TYPE("type"); const string USER("user"); +const string HA_BROKER("habroker"); const string AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER("agent.ind.event.org_apache_qpid_broker.#"); const string QMF2("qmf2"); @@ -100,6 +103,7 @@ const string _PACKAGE_NAME("_package_name"); const string _SCHEMA_ID("_schema_id"); const string OBJECT("OBJECT"); const string ORG_APACHE_QPID_BROKER("org.apache.qpid.broker"); +const string ORG_APACHE_QPID_HA("org.apache.qpid.ha"); const string QMF_DEFAULT_DIRECT("qmf.default.direct"); const string _QUERY_REQUEST("_query_request"); const string BROKER("broker"); @@ -113,36 +117,13 @@ template <class T> bool match(Variant::Map& schema) { return T::match(schema[CLASS_NAME], schema[PACKAGE_NAME]); } -enum ReplicateLevel { RL_NONE=0, RL_CONFIGURATION, RL_MESSAGES }; -const string S_NONE="none"; -const string S_CONFIGURATION="configuration"; -const string S_MESSAGES="messages"; - -ReplicateLevel replicateLevel(const string& level) { - if (level == S_NONE) return RL_NONE; - if (level == S_CONFIGURATION) return RL_CONFIGURATION; - if (level == S_MESSAGES) return RL_MESSAGES; - throw Exception("Invalid value for "+QPID_REPLICATE+": "+level); -} - -ReplicateLevel replicateLevel(const framing::FieldTable& f) { - if (f.isSet(QPID_REPLICATE)) return replicateLevel(f.getAsString(QPID_REPLICATE)); - else return RL_NONE; -} - -ReplicateLevel replicateLevel(const Variant::Map& m) { - Variant::Map::const_iterator i = m.find(QPID_REPLICATE); - if (i != m.end()) return replicateLevel(i->second.asString()); - else return RL_NONE; -} - -void sendQuery(const string className, const string& queueName, SessionHandler& sessionHandler) { +void sendQuery(const string& packageName, const string& className, const string& queueName, SessionHandler& sessionHandler) { framing::AMQP_ServerProxy peer(sessionHandler.out); Variant::Map request; request[_WHAT] = OBJECT; Variant::Map schema; schema[_CLASS_NAME] = className; - schema[_PACKAGE_NAME] = ORG_APACHE_QPID_BROKER; + schema[_PACKAGE_NAME] = packageName; request[_SCHEMA_ID] = schema; AMQFrame method((MessageTransferBody(ProtocolVersion(), QMF_DEFAULT_DIRECT, 0, 0))); @@ -181,13 +162,34 @@ Variant::Map asMapVoid(const Variant& value) { } // namespace + +ReplicateLevel BrokerReplicator::replicateLevel(const std::string& str) { + ReplicateLevel rl; + if (qpid::ha::replicateLevel(str, rl)) return rl; + else return haBroker.getSettings().replicateDefault; +} + +ReplicateLevel BrokerReplicator::replicateLevel(const framing::FieldTable& f) { + if (f.isSet(QPID_REPLICATE)) + return replicateLevel(f.getAsString(QPID_REPLICATE)); + else + return haBroker.getSettings().replicateDefault; +} + +ReplicateLevel BrokerReplicator::replicateLevel(const Variant::Map& m) { + Variant::Map::const_iterator i = m.find(QPID_REPLICATE); + if (i != m.end()) + return replicateLevel(i->second.asString()); + else + return haBroker.getSettings().replicateDefault; +} + BrokerReplicator::~BrokerReplicator() {} -BrokerReplicator::BrokerReplicator(const boost::shared_ptr<Link>& l) - : Exchange(QPID_CONFIGURATION_REPLICATOR), broker(*l->getBroker()), link(l) +BrokerReplicator::BrokerReplicator(HaBroker& hb, const boost::shared_ptr<Link>& l) + : Exchange(QPID_CONFIGURATION_REPLICATOR), + haBroker(hb), broker(hb.getBroker()), link(l) { - QPID_LOG(info, "HA: Backup replicating from " << - link->getTransport() << ":" << link->getHost() << ":" << link->getPort()); broker.getLinks().declare( link->getHost(), link->getPort(), false, // durable @@ -211,22 +213,26 @@ void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionH const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs()); //declare and bind an event queue - peer.getQueue().declare(queueName, "", false, false, true, true, FieldTable()); + FieldTable declareArgs; + declareArgs.setString(QPID_REPLICATE, str(RL_NONE)); + peer.getQueue().declare(queueName, "", false, false, true, true, declareArgs); peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER, FieldTable()); //subscribe to the queue peer.getMessage().subscribe(queueName, args.i_dest, 1, 0, false, "", 0, FieldTable()); peer.getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); peer.getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); - //issue a query request for queues and another for exchanges using event queue as the reply-to address - sendQuery(QUEUE, queueName, sessionHandler); - sendQuery(EXCHANGE, queueName, sessionHandler); - sendQuery(BINDING, queueName, sessionHandler); - QPID_LOG(debug, "HA: Backup activated configuration bridge: " << queueName); + // Issue a query request for queues, exchanges, bindings and the habroker + // using event queue as the reply-to address + sendQuery(ORG_APACHE_QPID_HA, HA_BROKER, queueName, sessionHandler); + sendQuery(ORG_APACHE_QPID_BROKER, QUEUE, queueName, sessionHandler); + sendQuery(ORG_APACHE_QPID_BROKER, EXCHANGE, queueName, sessionHandler); + sendQuery(ORG_APACHE_QPID_BROKER, BINDING, queueName, sessionHandler); + QPID_LOG(debug, "HA: Backup configuration bridge: " << queueName); } -// FIXME aconway 2011-12-02: error handling in route. -void BrokerReplicator::route(Deliverable& msg, const string& /*key*/, const framing::FieldTable* headers) { +void BrokerReplicator::route(Deliverable& msg) { + const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders(); Variant::List list; try { if (!isQMFv2(msg.getMessage()) || !headers) @@ -238,6 +244,7 @@ void BrokerReplicator::route(Deliverable& msg, const string& /*key*/, const fram if (headers->getAsString(QMF_CONTENT) == EVENT) { for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) { Variant::Map& map = i->asMap(); + QPID_LOG(trace, "HA: Backup received event: " << map); Variant::Map& schema = map[SCHEMA_ID].asMap(); Variant::Map& values = map[VALUES].asMap(); if (match<EventQueueDeclare>(schema)) doEventQueueDeclare(values); @@ -249,19 +256,22 @@ void BrokerReplicator::route(Deliverable& msg, const string& /*key*/, const fram } } else if (headers->getAsString(QMF_OPCODE) == QUERY_RESPONSE) { for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) { - string type = i->asMap()[SCHEMA_ID].asMap()[CLASS_NAME]; - Variant::Map& values = i->asMap()[VALUES].asMap(); + Variant::Map& map = i->asMap(); + QPID_LOG(trace, "HA: Backup received event: " << map); + string type = map[SCHEMA_ID].asMap()[CLASS_NAME]; + Variant::Map& values = map[VALUES].asMap(); framing::FieldTable args; amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); if (type == QUEUE) doResponseQueue(values); else if (type == EXCHANGE) doResponseExchange(values); else if (type == BINDING) doResponseBind(values); - else QPID_LOG(error, "HA: Backup received unknown response type=" << type - << " values=" << values); + else if (type == HA_BROKER) doResponseHaBroker(values); } - } else QPID_LOG(error, "HA: Backup received unexpected message: " << *headers); + } } catch (const std::exception& e) { - QPID_LOG(error, "HA: Backup replication error: " << e.what() << ": while handling: " << list); + QPID_LOG(critical, "HA: Backup configuration failed: " << e.what() + << ": while handling: " << list); + throw; } } @@ -282,15 +292,13 @@ void BrokerReplicator::doEventQueueDeclare(Variant::Map& values) { values[USER].asString(), values[RHOST].asString()); if (result.second) { - // FIXME aconway 2011-11-22: should delete old queue and - // re-create from event. - // Events are always up to date, whereas responses may be - // out of date. - QPID_LOG(debug, "HA: Backup created queue: " << name); + QPID_LOG(debug, "HA: Backup queue declare event: " << name); startQueueReplicator(result.first); } else { // FIXME aconway 2011-12-02: what's the right way to handle this? - QPID_LOG(warning, "HA: Backup queue already exists: " << name); + // Should we delete the old & re-create form the event? Responses + // may be old but events are always up-to-date. + QPID_LOG(warning, "HA: Backup queue declare event, already exists: " << name); } } } @@ -300,8 +308,11 @@ void BrokerReplicator::doEventQueueDelete(Variant::Map& values) { // sessions may be closed by a "queue deleted" exception. string name = values[QNAME].asString(); boost::shared_ptr<Queue> queue = broker.getQueues().find(name); - if (queue && replicateLevel(queue->getSettings())) { - QPID_LOG(debug, "HA: Backup deleting queue: " << name); + if (!queue) { + QPID_LOG(warning, "HA: Backup queue delete event, does not exist: " << name); + } else if (!replicateLevel(queue->getSettings())) { + QPID_LOG(warning, "HA: Backup queue delete event, not replicated: " << name); + } else { string rname = QueueReplicator::replicatorName(name); boost::shared_ptr<broker::Exchange> ex = broker.getExchanges().find(rname); boost::shared_ptr<QueueReplicator> qr = boost::dynamic_pointer_cast<QueueReplicator>(ex); @@ -310,6 +321,7 @@ void BrokerReplicator::doEventQueueDelete(Variant::Map& values) { // actually be destroyed, deleting the exhange broker.getExchanges().destroy(rname); broker.deleteQueue(name, values[USER].asString(), values[RHOST].asString()); + QPID_LOG(debug, "HA: Backup queue delete event: " << name); } } @@ -328,27 +340,29 @@ void BrokerReplicator::doEventExchangeDeclare(Variant::Map& values) { values[USER].asString(), values[RHOST].asString()).second) { - QPID_LOG(debug, "HA: Backup created exchange: " << name); + QPID_LOG(debug, "HA: Backup exchange declare event: " << name); } else { - // FIXME aconway 2011-11-22: should delete pre-exisitng exchange + // FIXME aconway 2011-11-22: should delete pre-existing exchange // and re-create from event. See comment in doEventQueueDeclare. - QPID_LOG(warning, "HA: Backup exchange already exists: " << name); + QPID_LOG(debug, "HA: Backup exchange declare event, already exists: " << name); } } } void BrokerReplicator::doEventExchangeDelete(Variant::Map& values) { string name = values[EXNAME].asString(); - try { - boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name); - if (exchange && replicateLevel(exchange->getArgs())) { - QPID_LOG(debug, "HA: Backup deleting exchange:" << name); - broker.deleteExchange( - name, - values[USER].asString(), - values[RHOST].asString()); - } - } catch (const framing::NotFoundException&) {} + boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name); + if (!exchange) { + QPID_LOG(warning, "HA: Backup exchange delete event, does not exist: " << name); + } else if (!replicateLevel(exchange->getArgs())) { + QPID_LOG(warning, "HA: Backup exchange delete event, not replicated: " << name); + } else { + QPID_LOG(debug, "HA: Backup exchange delete event:" << name); + broker.deleteExchange( + name, + values[USER].asString(), + values[RHOST].asString()); + } } void BrokerReplicator::doEventBind(Variant::Map& values) { @@ -364,10 +378,10 @@ void BrokerReplicator::doEventBind(Variant::Map& values) { framing::FieldTable args; amqp_0_10::translate(asMapVoid(values[ARGS]), args); string key = values[KEY].asString(); - QPID_LOG(debug, "HA: Backup replicated binding exchange=" << exchange->getName() + exchange->bind(queue, key, &args); + QPID_LOG(debug, "HA: Backup bind event: exchange=" << exchange->getName() << " queue=" << queue->getName() << " key=" << key); - exchange->bind(queue, key, &args); } } @@ -384,15 +398,14 @@ void BrokerReplicator::doEventUnbind(Variant::Map& values) { framing::FieldTable args; amqp_0_10::translate(asMapVoid(values[ARGS]), args); string key = values[KEY].asString(); - QPID_LOG(debug, "HA: Backup replicated unbinding exchange=" << exchange->getName() + exchange->unbind(queue, key, &args); + QPID_LOG(debug, "HA: Backup unbind event: exchange=" << exchange->getName() << " queue=" << queue->getName() << " key=" << key); - exchange->unbind(queue, key, &args); } } void BrokerReplicator::doResponseQueue(Variant::Map& values) { - // FIXME aconway 2011-11-22: more flexible ways & defaults to indicate replication Variant::Map argsMap(asMapVoid(values[ARGUMENTS])); if (!replicateLevel(argsMap)) return; framing::FieldTable args; @@ -409,12 +422,12 @@ void BrokerReplicator::doResponseQueue(Variant::Map& values) { ""/*TODO: who is the user?*/, ""/*TODO: what should we use as connection id?*/); if (result.second) { - QPID_LOG(debug, "HA: Backup created catch-up queue: " << values[NAME]); + QPID_LOG(debug, "HA: Backup queue response: " << name); startQueueReplicator(result.first); } else { // FIXME aconway 2011-11-22: Normal to find queue already // exists if we're failing over. - QPID_LOG(warning, "HA: Backup catch-up queue already exists: " << name); + QPID_LOG(warning, "HA: Backup queue response, already exists: " << name); } } @@ -432,9 +445,10 @@ void BrokerReplicator::doResponseExchange(Variant::Map& values) { ""/*TODO: who is the user?*/, ""/*TODO: what should we use as connection id?*/).second) { - QPID_LOG(debug, "HA: Backup catch-up exchange: " << values[NAME]); + QPID_LOG(debug, "HA: Backup exchange response: " << values[NAME].asString()); } else { - QPID_LOG(warning, "HA: Backup catch-up exchange already exists: " << values[QNAME]); + QPID_LOG(warning, "HA: Backup exchange query, already exists: " << + values[QNAME].asString()); } } @@ -464,7 +478,6 @@ void BrokerReplicator::doResponseBind(Variant::Map& values) { std::string qName = getRefName(QUEUE_REF_PREFIX, values[QUEUE_REF]); boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(exName); boost::shared_ptr<Queue> queue = broker.getQueues().find(qName); - // FIXME aconway 2011-11-24: more flexible configuration for binding replication. // Automatically replicate binding if queue and exchange exist and are replicated if (exchange && replicateLevel(exchange->getArgs()) && @@ -474,16 +487,39 @@ void BrokerReplicator::doResponseBind(Variant::Map& values) { amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args); string key = values[KEY].asString(); exchange->bind(queue, key, &args); - QPID_LOG(debug, "HA: Backup catch-up binding: exchange=" << exchange->getName() + QPID_LOG(debug, "HA: Backup bind response: exchange=" << exchange->getName() << " queue=" << queue->getName() << " key=" << key); } } +namespace { +const string REPLICATE_DEFAULT="replicateDefault"; +} + +// Received the ha-broker configuration object for the primary broker. +void BrokerReplicator::doResponseHaBroker(Variant::Map& values) { + try { + ReplicateLevel mine = haBroker.getSettings().replicateDefault; + ReplicateLevel primary = replicateLevel(values[REPLICATE_DEFAULT].asString()); + if (mine != primary) { + std::ostringstream os; + os << "Replicate default on backup (" << mine + << ") does not match primary (" << primary << ")"; + haBroker.shutdown(os.str()); + } + } catch (const std::exception& e) { + std::ostringstream os; + os << "Received invalid replicate default from primary: " << e.what(); + haBroker.shutdown(os.str()); + } +} + void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue) { - if (replicateLevel(queue->getSettings()) == RL_MESSAGES) { + if (replicateLevel(queue->getSettings()) == RL_ALL) { boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link)); - broker.getExchanges().registerExchange(qr); + if (!broker.getExchanges().registerExchange(qr)) + throw Exception(QPID_MSG("Duplicate queue replicator " << qr->getName())); qr->activate(); } } diff --git a/cpp/src/qpid/ha/BrokerReplicator.h b/cpp/src/qpid/ha/BrokerReplicator.h index cfb6cf9a28..c9d7b9f74c 100644 --- a/cpp/src/qpid/ha/BrokerReplicator.h +++ b/cpp/src/qpid/ha/BrokerReplicator.h @@ -22,6 +22,7 @@ * */ +#include "ReplicateLevel.h" #include "qpid/broker/Exchange.h" #include "qpid/types/Variant.h" #include <boost/shared_ptr.hpp> @@ -35,7 +36,12 @@ class Bridge; class SessionHandler; } +namespace framing { +class FieldTable; +} + namespace ha { +class HaBroker; /** * Replicate configuration on a backup broker. @@ -51,19 +57,23 @@ namespace ha { class BrokerReplicator : public broker::Exchange { public: - BrokerReplicator(const boost::shared_ptr<broker::Link>&); + BrokerReplicator(HaBroker&, const boost::shared_ptr<broker::Link>&); ~BrokerReplicator(); std::string getType() const; // Exchange methods bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*); bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*); - void route(broker::Deliverable&, const std::string&, const framing::FieldTable*); + void route(broker::Deliverable&); bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const); private: void initializeBridge(broker::Bridge&, broker::SessionHandler&); + ReplicateLevel replicateLevel(const std::string&); + ReplicateLevel replicateLevel(const framing::FieldTable& args); + ReplicateLevel replicateLevel(const types::Variant::Map& args); + void doEventQueueDeclare(types::Variant::Map& values); void doEventQueueDelete(types::Variant::Map& values); void doEventExchangeDeclare(types::Variant::Map& values); @@ -74,9 +84,11 @@ class BrokerReplicator : public broker::Exchange void doResponseQueue(types::Variant::Map& values); void doResponseExchange(types::Variant::Map& values); void doResponseBind(types::Variant::Map& values); + void doResponseHaBroker(types::Variant::Map& values); void startQueueReplicator(const boost::shared_ptr<broker::Queue>&); + HaBroker& haBroker; broker::Broker& broker; boost::shared_ptr<broker::Link> link; }; diff --git a/cpp/src/qpid/ha/HaBroker.cpp b/cpp/src/qpid/ha/HaBroker.cpp index 0d3bd51439..7d82fb63bd 100644 --- a/cpp/src/qpid/ha/HaBroker.cpp +++ b/cpp/src/qpid/ha/HaBroker.cpp @@ -25,10 +25,15 @@ #include "ReplicatingSubscription.h" #include "qpid/Exception.h" #include "qpid/broker/Broker.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/SignalHandler.h" #include "qpid/management/ManagementAgent.h" #include "qmf/org/apache/qpid/ha/Package.h" -#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetClientAddresses.h" -#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokerAddresses.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerReplicate.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokers.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetPublicBrokers.h" +#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetExpectedBackups.h" #include "qpid/log/Statement.h" namespace qpid { @@ -40,8 +45,10 @@ using namespace std; namespace { -const std::string PRIMARY="primary"; +const std::string STANDALONE="standalone"; +const std::string CATCH_UP="catch-up"; const std::string BACKUP="backup"; +const std::string PRIMARY="primary"; } // namespace @@ -49,7 +56,6 @@ const std::string BACKUP="backup"; HaBroker::HaBroker(broker::Broker& b, const Settings& s) : broker(b), settings(s), - backup(new Backup(b, s)), mgmtObject(0) { // Register a factory for replicating subscriptions. @@ -62,15 +68,20 @@ HaBroker::HaBroker(broker::Broker& b, const Settings& s) ManagementAgent* ma = broker.getManagementAgent(); if (!ma) throw Exception("Cannot start HA: management is disabled"); - if (ma) { - _qmf::Package packageInit(ma); - mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker"); - mgmtObject->set_status(BACKUP); - ma->addObject(mgmtObject); - } + _qmf::Package packageInit(ma); + mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker"); + mgmtObject->set_status(settings.cluster ? BACKUP : STANDALONE); + mgmtObject->set_replicateDefault(str(settings.replicateDefault)); + ma->addObject(mgmtObject); + + // NOTE: lock is not needed in a constructor but we created it just to pass + // to the set functions. sys::Mutex::ScopedLock l(lock); if (!settings.clientUrl.empty()) setClientUrl(Url(settings.clientUrl), l); if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl), l); + + // If we are in a cluster, we start in backup mode. + if (settings.cluster) backup.reset(new Backup(*this, s)); } HaBroker::~HaBroker() {} @@ -80,26 +91,47 @@ Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args, switch (methodId) { case _qmf::HaBroker::METHOD_PROMOTE: { if (backup.get()) { // I am a backup - // FIXME aconway 2012-01-26: create primary state before resetting backup - // as that allows client connections. + // NOTE: resetting backup allows client connections, so any + // primary state should be set up here before backup.reset() backup.reset(); - QPID_LOG(notice, "HA: Primary promoted from backup"); + QPID_LOG(notice, "HA: Promoted to primary"); mgmtObject->set_status(PRIMARY); } break; } - case _qmf::HaBroker::METHOD_SETCLIENTADDRESSES: { - setClientUrl( - Url(dynamic_cast<_qmf::ArgsHaBrokerSetClientAddresses&>(args). - i_clientAddresses), l); + case _qmf::HaBroker::METHOD_SETBROKERS: { + setBrokerUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokers&>(args).i_url), l); + break; + } + case _qmf::HaBroker::METHOD_SETPUBLICBROKERS: { + setClientUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetPublicBrokers&>(args).i_url), l); break; } - case _qmf::HaBroker::METHOD_SETBROKERADDRESSES: { - setBrokerUrl( - Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokerAddresses&>(args) - .i_brokerAddresses), l); + case _qmf::HaBroker::METHOD_SETEXPECTEDBACKUPS: { + setExpectedBackups(dynamic_cast<_qmf::ArgsHaBrokerSetExpectedBackups&>(args).i_expectedBackups, l); + break; + } + case _qmf::HaBroker::METHOD_REPLICATE: { + _qmf::ArgsHaBrokerReplicate& bq_args = + dynamic_cast<_qmf::ArgsHaBrokerReplicate&>(args); + QPID_LOG(debug, "HA replicating individual queue "<< bq_args.i_queue << " from " << bq_args.i_broker); + + boost::shared_ptr<broker::Queue> queue = broker.getQueues().get(bq_args.i_queue); + Url url(bq_args.i_broker); + string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol; + std::pair<broker::Link::shared_ptr, bool> result = broker.getLinks().declare( + url[0].host, url[0].port, protocol, + false, // durable + settings.mechanism, settings.username, settings.password); + boost::shared_ptr<broker::Link> link = result.first; + link->setUrl(url); + // Create a queue replicator + boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link)); + qr->activate(); + broker.getExchanges().registerExchange(qr); break; } + default: return Manageable::STATUS_UNKNOWN_METHOD; } @@ -114,24 +146,35 @@ void HaBroker::setClientUrl(const Url& url, const sys::Mutex::ScopedLock& l) { void HaBroker::updateClientUrl(const sys::Mutex::ScopedLock&) { Url url = clientUrl.empty() ? brokerUrl : clientUrl; - assert(!url.empty()); - mgmtObject->set_clientAddresses(url.str()); + if (url.empty()) throw Url::Invalid("HA client URL is empty"); + mgmtObject->set_publicBrokers(url.str()); knownBrokers.clear(); knownBrokers.push_back(url); - QPID_LOG(debug, "HA: Setting client known-brokers to: " << url); + QPID_LOG(debug, "HA: Setting client URL to: " << url); } void HaBroker::setBrokerUrl(const Url& url, const sys::Mutex::ScopedLock& l) { - if (url.empty()) throw Exception("Invalid empty URL for HA broker failover"); + if (url.empty()) throw Url::Invalid("HA broker URL is empty"); + QPID_LOG(debug, "HA: Setting broker URL to: " << url); brokerUrl = url; - mgmtObject->set_brokerAddresses(brokerUrl.str()); + mgmtObject->set_brokers(brokerUrl.str()); if (backup.get()) backup->setBrokerUrl(brokerUrl); // Updating broker URL also updates defaulted client URL: if (clientUrl.empty()) updateClientUrl(l); } +void HaBroker::setExpectedBackups(size_t n, const sys::Mutex::ScopedLock&) { + expectedBackups = n; + mgmtObject->set_expectedBackups(n); +} + std::vector<Url> HaBroker::getKnownBrokers() const { return knownBrokers; } +void HaBroker::shutdown(const std::string& message) { + QPID_LOG(critical, "Shutting down: " << message); + broker.shutdown(); +} + }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/HaBroker.h b/cpp/src/qpid/ha/HaBroker.h index 835a47c749..99b30fd36b 100644 --- a/cpp/src/qpid/ha/HaBroker.h +++ b/cpp/src/qpid/ha/HaBroker.h @@ -52,9 +52,16 @@ class HaBroker : public management::Manageable management::Manageable::status_t ManagementMethod ( uint32_t methodId, management::Args& args, std::string& text); + broker::Broker& getBroker() { return broker; } + const Settings& getSettings() const { return settings; } + + // Log a critical error message and shut down the broker. + void shutdown(const std::string& message); + private: void setClientUrl(const Url&, const sys::Mutex::ScopedLock&); void setBrokerUrl(const Url&, const sys::Mutex::ScopedLock&); + void setExpectedBackups(size_t, const sys::Mutex::ScopedLock&); void updateClientUrl(const sys::Mutex::ScopedLock&); bool isPrimary(const sys::Mutex::ScopedLock&) { return !backup.get(); } std::vector<Url> getKnownBrokers() const; @@ -67,6 +74,7 @@ class HaBroker : public management::Manageable qmf::org::apache::qpid::ha::HaBroker* mgmtObject; Url clientUrl, brokerUrl; std::vector<Url> knownBrokers; + size_t expectedBackups; }; }} // namespace qpid::ha diff --git a/cpp/src/qpid/ha/HaPlugin.cpp b/cpp/src/qpid/ha/HaPlugin.cpp index fc9e48411d..4da3b0d7d2 100644 --- a/cpp/src/qpid/ha/HaPlugin.cpp +++ b/cpp/src/qpid/ha/HaPlugin.cpp @@ -31,12 +31,23 @@ struct Options : public qpid::Options { Settings& settings; Options(Settings& s) : qpid::Options("HA Options"), settings(s) { addOptions() - ("ha-enable", optValue(settings.enabled, "yes|no"), "Enable High Availability features") - ("ha-client-url", optValue(settings.clientUrl,"URL"), "URL that clients use to connect and fail over.") - ("ha-broker-url", optValue(settings.brokerUrl,"URL"), "URL that backup brokers use to connect and fail over.") - ("ha-username", optValue(settings.username, "USER"), "Username for connections between brokers") - ("ha-password", optValue(settings.password, "PASS"), "Password for connections between brokers") - ("ha-mechanism", optValue(settings.mechanism, "MECH"), "Authentication mechanism for connections between brokers") + ("ha-cluster", optValue(settings.cluster, "yes|no"), + "Join a HA active/passive cluster.") + ("ha-brokers", optValue(settings.brokerUrl,"URL"), + "URL that backup brokers use to connect and fail over.") + ("ha-public-brokers", optValue(settings.clientUrl,"URL"), + "URL that clients use to connect and fail over, defaults to ha-brokers.") + ("ha-replicate", + optValue(settings.replicateDefault, "LEVEL"), + "Replication level for creating queues and exchanges if there is no qpid.replicate argument supplied. LEVEL is 'none', 'configuration' or 'all'") + ("ha-expected-backups", optValue(settings.expectedBackups, "N"), + "Number of backups expected to be active in the HA cluster.") + ("ha-username", optValue(settings.username, "USER"), + "Username for connections between HA brokers") + ("ha-password", optValue(settings.password, "PASS"), + "Password for connections between HA brokers") + ("ha-mechanism", optValue(settings.mechanism, "MECH"), + "Authentication mechanism for connections between HA brokers") ; } }; @@ -55,10 +66,7 @@ struct HaPlugin : public Plugin { void initialize(Plugin::Target& target) { broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); - if (broker && settings.enabled) { - haBroker.reset(new ha::HaBroker(*broker, settings)); - } else - QPID_LOG(notice, "HA: Disabled"); + if (broker) haBroker.reset(new ha::HaBroker(*broker, settings)); } }; diff --git a/cpp/src/qpid/ha/QueueReplicator.cpp b/cpp/src/qpid/ha/QueueReplicator.cpp index 0017cc82cd..633619be13 100644 --- a/cpp/src/qpid/ha/QueueReplicator.cpp +++ b/cpp/src/qpid/ha/QueueReplicator.cpp @@ -30,8 +30,8 @@ #include "qpid/framing/SequenceSet.h" #include "qpid/framing/FieldTable.h" #include "qpid/log/Statement.h" +#include "qpid/Msg.h" #include <boost/shared_ptr.hpp> -#include <sstream> namespace { const std::string QPID_REPLICATOR_("qpid.replicator-"); @@ -54,10 +54,8 @@ std::string QueueReplicator::replicatorName(const std::string& queueName) { QueueReplicator::QueueReplicator(boost::shared_ptr<Queue> q, boost::shared_ptr<Link> l) : Exchange(replicatorName(q->getName()), 0, q->getBroker()), queue(q), link(l) { - std::stringstream ss; - ss << "HA: Backup " << queue->getName() << ": "; - logPrefix = ss.str(); - QPID_LOG(info, logPrefix << "Created, settings: " << q->getSettings()); + logPrefix = "HA: Backup of " + queue->getName() + ": "; + QPID_LOG(info, logPrefix << "Created"); } // This must be separate from the constructor so we can call shared_from_this. @@ -77,7 +75,7 @@ void QueueReplicator::activate() { 0, // sync? // Include shared_ptr to self to ensure we are not deleted // before initializeBridge is called. - boost::bind(&QueueReplicator::initializeBridge, this, _1, _2, shared_from_this()) + boost::bind(&QueueReplicator::initializeBridge, shared_from_this(), _1, _2) ); } @@ -91,9 +89,7 @@ void QueueReplicator::deactivate() { } // Called in a broker connection thread when the bridge is created. -// shared_ptr to self ensures we are not deleted before initializeBridge is called. -void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler, - boost::shared_ptr<QueueReplicator> /*self*/) { +void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler) { sys::Mutex::ScopedLock l(lock); bridgeName = bridge.getName(); framing::AMQP_ServerProxy peer(sessionHandler.out); @@ -141,27 +137,35 @@ void QueueReplicator::dequeue(SequenceNumber n, const sys::Mutex::ScopedLock&) } // Called in connection thread of the queues bridge to primary. -void QueueReplicator::route(Deliverable& msg, const std::string& key, const FieldTable*) +void QueueReplicator::route(Deliverable& msg) { - sys::Mutex::ScopedLock l(lock); - if (key == DEQUEUE_EVENT_KEY) { - SequenceSet dequeues = decodeContent<SequenceSet>(msg.getMessage()); - QPID_LOG(trace, logPrefix << "Dequeue: " << dequeues); - //TODO: should be able to optimise the following - for (SequenceSet::iterator i = dequeues.begin(); i != dequeues.end(); i++) - dequeue(*i, l); - } else if (key == POSITION_EVENT_KEY) { - SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage()); - QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition() - << " to " << position); - assert(queue->getPosition() <= position); - //TODO aconway 2011-12-14: Optimize this? - for (SequenceNumber i = queue->getPosition(); i < position; ++i) - dequeue(i,l); - queue->setPosition(position); - } else { - msg.deliverTo(queue); - QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition()); + try { + const std::string& key = msg.getMessage().getRoutingKey(); + sys::Mutex::ScopedLock l(lock); + if (key == DEQUEUE_EVENT_KEY) { + SequenceSet dequeues = decodeContent<SequenceSet>(msg.getMessage()); + QPID_LOG(trace, logPrefix << "Dequeue: " << dequeues); + //TODO: should be able to optimise the following + for (SequenceSet::iterator i = dequeues.begin(); i != dequeues.end(); i++) + dequeue(*i, l); + } else if (key == POSITION_EVENT_KEY) { + SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage()); + QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition() + << " to " << position); + if (queue->getPosition() > position) { + throw Exception( + QPID_MSG(logPrefix << "Invalid position update from " + << queue->getPosition() << " to " << position)); + } + queue->setPosition(position); + } else { + msg.deliverTo(queue); + QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition()); + } + } + catch (const std::exception& e) { + QPID_LOG(critical, logPrefix << "Replication failed: " << e.what()); + throw; } } diff --git a/cpp/src/qpid/ha/QueueReplicator.h b/cpp/src/qpid/ha/QueueReplicator.h index 9de7dd480c..bcbac988fa 100644 --- a/cpp/src/qpid/ha/QueueReplicator.h +++ b/cpp/src/qpid/ha/QueueReplicator.h @@ -66,12 +66,11 @@ class QueueReplicator : public broker::Exchange, bool bind(boost::shared_ptr<broker::Queue >, const std::string&, const framing::FieldTable*); bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*); - void route(broker::Deliverable&, const std::string&, const framing::FieldTable*); + void route(broker::Deliverable&); bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const); private: - void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler, - boost::shared_ptr<QueueReplicator> self); + void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler); void dequeue(framing::SequenceNumber, const sys::Mutex::ScopedLock&); std::string logPrefix; diff --git a/cpp/src/qpid/ha/ReplicateLevel.cpp b/cpp/src/qpid/ha/ReplicateLevel.cpp new file mode 100644 index 0000000000..4981577225 --- /dev/null +++ b/cpp/src/qpid/ha/ReplicateLevel.cpp @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ReplicateLevel.h" +#include "qpid/Exception.h" +#include "qpid/Msg.h" +#include <iostream> +#include <assert.h> + +namespace qpid { +namespace ha { + +using namespace std; + +// Note replicateLevel is called during plugin-initialization which +// happens in the static construction phase so these constants need +// to be POD, they can't be class objects +// +namespace { +const char* S_NONE="none"; +const char* S_CONFIGURATION="configuration"; +const char* S_ALL="all"; +} + +bool replicateLevel(const string& level, ReplicateLevel& out) { + if (level == S_NONE) { out = RL_NONE; return true; } + if (level == S_CONFIGURATION) { out = RL_CONFIGURATION; return true; } + if (level == S_ALL) { out = RL_ALL; return true; } + return false; +} + +ReplicateLevel replicateLevel(const string& level) { + ReplicateLevel rl; + if (!replicateLevel(level, rl)) + throw Exception("Invalid value for replication level: "+level); + return rl; +} + +string str(ReplicateLevel l) { + const char* names[] = { S_NONE, S_CONFIGURATION, S_ALL }; + if (l > RL_ALL) + throw Exception(QPID_MSG("Invalid value for replication level: " << l)); + return names[l]; +} + +ostream& operator<<(ostream& o, ReplicateLevel rl) { return o << str(rl); } + +istream& operator>>(istream& i, ReplicateLevel& rl) { + string str; + i >> str; + rl = replicateLevel(str); + return i; +} + +}} // namespace qpid::ha diff --git a/cpp/src/qpid/framing/BodyHandler.h b/cpp/src/qpid/ha/ReplicateLevel.h index 9ded737195..c11e03f0ce 100644 --- a/cpp/src/qpid/framing/BodyHandler.h +++ b/cpp/src/qpid/ha/ReplicateLevel.h @@ -1,5 +1,5 @@ -#ifndef _BodyHandler_ -#define _BodyHandler_ +#ifndef QPID_HA_REPLICATELEVEL_H +#define QPID_HA_REPLICATELEVEL_H /* * @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,35 +22,31 @@ * */ -#include <boost/shared_ptr.hpp> +#include <string> +#include <iosfwd> namespace qpid { -namespace framing { -class AMQBody; -class AMQMethodBody; -class AMQHeaderBody; -class AMQContentBody; -class AMQHeartbeatBody; +namespace ha { + +enum ReplicateLevel { RL_NONE, RL_CONFIGURATION, RL_ALL }; -// TODO aconway 2007-08-10: rework using Visitor pattern? +/** + * If str is a valid replicate level, set out and return true. + */ +bool replicateLevel(const std::string& str, ReplicateLevel& out); /** - * Interface to handle incoming frame bodies. - * Derived classes provide logic for each frame type. + *@return enum corresponding to string level. + *@throw qpid::Exception if level is not a valid replication level. */ -class BodyHandler { - public: - virtual ~BodyHandler(); - virtual void handleBody(AMQBody* body); +ReplicateLevel replicateLevel(const std::string& level); - protected: - virtual void handleMethod(AMQMethodBody*) = 0; - virtual void handleHeader(AMQHeaderBody*) = 0; - virtual void handleContent(AMQContentBody*) = 0; - virtual void handleHeartbeat(AMQHeartbeatBody*) = 0; -}; +/**@return string form of replicate level */ +std::string str(ReplicateLevel l); -}} +std::ostream& operator<<(std::ostream&, ReplicateLevel); +std::istream& operator>>(std::istream&, ReplicateLevel&); +}} // namespaces qpid::ha -#endif +#endif /*!QPID_HA_REPLICATELEVEL_H*/ diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.cpp b/cpp/src/qpid/ha/ReplicatingSubscription.cpp index af6180305d..91a4538bc4 100644 --- a/cpp/src/qpid/ha/ReplicatingSubscription.cpp +++ b/cpp/src/qpid/ha/ReplicatingSubscription.cpp @@ -87,10 +87,13 @@ ReplicatingSubscription::ReplicatingSubscription( events(new Queue(mask(name))), consumer(new DelegatingConsumer(*this)) { + // Separate the remote part from a "local-remote" address. + string address = parent->getSession().getConnection().getUrl(); + size_t i = address.find('-'); + if (i != string::npos) address = address.substr(i+1); + logPrefix = "HA: Primary "; stringstream ss; - ss << "HA: Primary: " << getQueue()->getName() << " at " - << parent->getSession().getConnection().getUrl() << ": "; - logPrefix = ss.str(); + logSuffix = " (" + address + ")"; // FIXME aconway 2011-12-09: Failover optimization removed. // There was code here to re-use messages already on the backup @@ -99,7 +102,7 @@ ReplicatingSubscription::ReplicatingSubscription( // can be re-introduced later. Last revision with the optimization: // r1213258 | QPID-3603: Fix QueueReplicator subscription parameters. - QPID_LOG(debug, logPrefix << "Created backup subscription " << getName()); + QPID_LOG(debug, logPrefix << "created backup subscription " << getName() << logSuffix); // FIXME aconway 2011-12-15: ConsumerImpl::position is left at 0 // so we will start consuming from the lowest numbered message. @@ -109,23 +112,36 @@ ReplicatingSubscription::ReplicatingSubscription( // Message is delivered in the subscription's connection thread. bool ReplicatingSubscription::deliver(QueuedMessage& m) { - // Add position events for the subscribed queue, not for the internal event queue. - if (m.queue && m.queue == getQueue().get()) { - sys::Mutex::ScopedLock l(lock); - assert(position == m.position); - // m.position is the position of the newly enqueued m on the local queue. - // backupPosition is latest position on the backup queue (before enqueueing m.) - assert(m.position > backupPosition); - if (m.position - backupPosition > 1) { - // Position has advanced because of messages dequeued ahead of us. - SequenceNumber send(m.position); - --send; // Send the position before m was enqueued. - sendPositionEvent(send, l); + try { + // Add position events for the subscribed queue, not for the internal event queue. + if (m.queue && m.queue == getQueue().get()) { + sys::Mutex::ScopedLock l(lock); + if (position != m.position) + throw Exception( + QPID_MSG("Expected position " << position + << " but got " << m.position)); + // m.position is the position of the newly enqueued m on the local queue. + // backupPosition is latest position on the backup queue (before enqueueing m.) + if (m.position <= backupPosition) + throw Exception( + QPID_MSG("Expected position > " << backupPosition + << " but got " << m.position)); + + if (m.position - backupPosition > 1) { + // Position has advanced because of messages dequeued ahead of us. + SequenceNumber send(m.position); + --send; // Send the position before m was enqueued. + sendPositionEvent(send, l); + } + backupPosition = m.position; + QPID_LOG(trace, logPrefix << "replicating " << m << logSuffix); } - backupPosition = m.position; - QPID_LOG(trace, logPrefix << "Replicating message " << m.position); + return ConsumerImpl::deliver(m); + } catch (const std::exception& e) { + QPID_LOG(critical, logPrefix << "error replicating " << getQueue()->getName() + << logSuffix << ": " << e.what()); + throw; } - return ConsumerImpl::deliver(m); } ReplicatingSubscription::~ReplicatingSubscription() {} @@ -139,7 +155,7 @@ void ReplicatingSubscription::complete( { // Handle completions for the subscribed queue, not the internal event queue. if (qm.queue && qm.queue == getQueue().get()) { - QPID_LOG(trace, logPrefix << "Completed message " << qm.position); + QPID_LOG(trace, logPrefix << "completed " << qm << logSuffix); Delayed::iterator i= delayed.find(qm.position); // The same message can be completed twice, by acknowledged and // dequeued, remove it from the set so it only gets completed @@ -157,7 +173,7 @@ void ReplicatingSubscription::complete( void ReplicatingSubscription::enqueued(const QueuedMessage& qm) { sys::Mutex::ScopedLock l(lock); // Delay completion - QPID_LOG(trace, logPrefix << "Delaying completion of message " << qm.position); + QPID_LOG(trace, logPrefix << "delaying completion of " << qm << logSuffix); qm.payload->getIngressCompletion().startCompleter(); assert(delayed.find(qm.position) == delayed.end()); delayed[qm.position] = qm; @@ -168,7 +184,7 @@ void ReplicatingSubscription::enqueued(const QueuedMessage& qm) { void ReplicatingSubscription::cancelComplete( const Delayed::value_type& v, const sys::Mutex::ScopedLock&) { - QPID_LOG(trace, logPrefix << "Cancel completed message " << v.second.position); + QPID_LOG(trace, logPrefix << "cancel completed " << v.second << logSuffix); v.second.payload->getIngressCompletion().finishCompleter(); } @@ -179,7 +195,7 @@ void ReplicatingSubscription::cancel() boost::dynamic_pointer_cast<QueueObserver>(shared_from_this())); { sys::Mutex::ScopedLock l(lock); - QPID_LOG(debug, logPrefix <<"Cancelled backup subscription " << getName()); + QPID_LOG(debug, logPrefix << "cancel backup subscription " << getName() << logSuffix); for_each(delayed.begin(), delayed.end(), boost::bind(&ReplicatingSubscription::cancelComplete, this, _1, boost::ref(l))); delayed.clear(); @@ -201,7 +217,8 @@ bool ReplicatingSubscription::hideDeletedError() { return true; } // Called with lock held. Called in subscription's connection thread. void ReplicatingSubscription::sendDequeueEvent(const sys::Mutex::ScopedLock& l) { - QPID_LOG(trace, logPrefix << "Sending dequeues " << dequeues); + QPID_LOG(trace, logPrefix << "sending dequeues " << dequeues + << " from " << getQueue()->getName() << logSuffix); string buf(dequeues.encodedSize(),'\0'); framing::Buffer buffer(&buf[0], buf.size()); dequeues.encode(buffer); @@ -216,7 +233,7 @@ void ReplicatingSubscription::dequeued(const QueuedMessage& qm) { { sys::Mutex::ScopedLock l(lock); - QPID_LOG(trace, logPrefix << "Dequeued message " << qm.position); + QPID_LOG(trace, logPrefix << "dequeued " << qm << logSuffix); dequeues.add(qm.position); // If we have not yet sent this message to the backup, then // complete it now as it will never be accepted. @@ -229,8 +246,8 @@ void ReplicatingSubscription::dequeued(const QueuedMessage& qm) void ReplicatingSubscription::sendPositionEvent( SequenceNumber position, const sys::Mutex::ScopedLock&l ) { - QPID_LOG(trace, logPrefix << "Sending position " << position - << ", was " << backupPosition); + QPID_LOG(trace, logPrefix << "sending position " << position + << ", was " << backupPosition << logSuffix); string buf(backupPosition.encodedSize(),'\0'); framing::Buffer buffer(&buf[0], buf.size()); position.encode(buffer); diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.h b/cpp/src/qpid/ha/ReplicatingSubscription.h index e311f9505a..f9176915f6 100644 --- a/cpp/src/qpid/ha/ReplicatingSubscription.h +++ b/cpp/src/qpid/ha/ReplicatingSubscription.h @@ -33,7 +33,7 @@ namespace qpid { namespace broker { class Message; class Queue; -class QueuedMessage; +struct QueuedMessage; class OwnershipToken; } @@ -94,7 +94,7 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl, bool doDispatch(); private: typedef std::map<framing::SequenceNumber, broker::QueuedMessage> Delayed; - std::string logPrefix; + std::string logPrefix, logSuffix; boost::shared_ptr<broker::Queue> events; boost::shared_ptr<broker::Consumer> consumer; Delayed delayed; diff --git a/cpp/src/qpid/ha/Settings.h b/cpp/src/qpid/ha/Settings.h index 049c873b9f..bf70c3f3f7 100644 --- a/cpp/src/qpid/ha/Settings.h +++ b/cpp/src/qpid/ha/Settings.h @@ -22,6 +22,7 @@ * */ +#include "ReplicateLevel.h" #include <string> namespace qpid { @@ -33,10 +34,12 @@ namespace ha { class Settings { public: - Settings() : enabled(false) {} - bool enabled; + Settings() : cluster(false), expectedBackups(0), replicateDefault(RL_NONE) {} + bool cluster; // True if we are a cluster member. std::string clientUrl; std::string brokerUrl; + size_t expectedBackups; + ReplicateLevel replicateDefault; std::string username, password, mechanism; private: }; diff --git a/cpp/src/qpid/ha/management-schema.xml b/cpp/src/qpid/ha/management-schema.xml index fe4a14d111..363dff61fb 100644 --- a/cpp/src/qpid/ha/management-schema.xml +++ b/cpp/src/qpid/ha/management-schema.xml @@ -22,16 +22,39 @@ <!-- Monitor and control HA status of a broker. --> <class name="HaBroker"> <property name="name" type="sstr" access="RC" index="y" desc="Primary Key"/> + <property name="status" type="sstr" desc="HA status: primary or backup"/> - <property name="clientAddresses" type="sstr" desc="List of addresses used by clients to connect to the HA cluster."/> - <property name="brokerAddresses" type="sstr" desc="List of addresses used by HA brokers to connect to each other."/> + + <property name="brokers" type="sstr" + desc="Multiple-address URL used by HA brokers to connect to each other."/> + + <property name="publicBrokers" type="sstr" + desc="Multiple-address URL used by clients to connect to the HA brokers."/> + + <property name="expectedBackups" type="uint16" + desc="Number of HA backup brokers expected."/> + + <property + name="replicateDefault" type="sstr" + desc="Replicate value for queues/exchanges without a qpid.replicate argument"/> <method name="promote" desc="Promote a backup broker to primary."/> - <method name="setClientAddresses" desc="Set HA client addresses"> - <arg name="clientAddresses" type="sstr" dir="I"/> + + <method name="setBrokers" desc="Set URL for HA brokers to connect to each other."> + <arg name="url" type="sstr" dir="I"/> </method> - <method name="setBrokerAddresses" desc="Set HA broker addresses"> - <arg name="brokerAddresses" type="sstr" dir="I"/> + + <method name="setPublicBrokers" desc="Set URL for clients to connect to HA brokers"> + <arg name="url" type="sstr" dir="I"/> + </method> + + <method name="setExpectedBackups" desc="Set number of backups expected"> + <arg name="expectedBackups" type="uint16" dir="I"/> + </method> + + <method name="replicate" desc="Replicate individual queue from remote broker."> + <arg name="broker" type="sstr" dir="I"/> + <arg name="queue" type="sstr" dir="I"/> </method> </class> diff --git a/cpp/src/qpid/log/Statement.cpp b/cpp/src/qpid/log/Statement.cpp index 7dfdf08703..79f7a28100 100644 --- a/cpp/src/qpid/log/Statement.cpp +++ b/cpp/src/qpid/log/Statement.cpp @@ -55,6 +55,50 @@ void Statement::log(const std::string& message) { } Statement::Initializer::Initializer(Statement& s) : statement(s) { + // QPID-3891 + // From the given BOOST_CURRENT_FUNCTION name extract only the + // namespace-qualified-functionName, skipping return and calling args. + // Given: + // <possible return value type> qpid::name::space::Function(args) + // Return: + // "qpid::name::space::Function". + if (s.function != NULL) { + bool foundOParen(false); + const char * opPtr; + for (opPtr = s.function; *opPtr != '\0'; opPtr++) { + if (*opPtr == '(') { + foundOParen = true; + break; + } + } + + if (foundOParen) { + const char * bPtr = opPtr; + for (bPtr = opPtr; bPtr > s.function; bPtr--) { + if (bPtr[-1] == ' ') { + break; + } + } + + size_t nStoreSize = opPtr - bPtr; + if (nStoreSize > 0) { + // Note: the struct into which this name is stored + // is static and is never deleted. + char * nStore = new char[nStoreSize + 1]; + std::copy (bPtr, opPtr, nStore); + nStore[nStoreSize] = '\0'; + + s.function = nStore; + } else { + // Ignore zero length name + } + } else { + // No name found - do nothing + } + } else { + // no function-name pointer to process + } + Logger::instance().add(s); } diff --git a/cpp/src/qpid/log/posix/SinkOptions.cpp b/cpp/src/qpid/log/posix/SinkOptions.cpp index ffa7633e3b..8459938e5c 100644 --- a/cpp/src/qpid/log/posix/SinkOptions.cpp +++ b/cpp/src/qpid/log/posix/SinkOptions.cpp @@ -30,6 +30,10 @@ using std::string; using qpid::Exception; +namespace qpid { +namespace log { +namespace posix { + namespace { // SyslogFacilities maps from syslog values to the text equivalents. @@ -110,10 +114,6 @@ std::string basename(const std::string path) { } // namespace -namespace qpid { -namespace log { -namespace posix { - std::ostream& operator<<(std::ostream& o, const SyslogFacility& f) { return o << SyslogFacilities().name(f.value); } diff --git a/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp index 741ef442b0..062a530706 100644 --- a/cpp/src/qpid/management/ManagementAgent.cpp +++ b/cpp/src/qpid/management/ManagementAgent.cpp @@ -30,6 +30,7 @@ #include "qpid/log/Statement.h" #include <qpid/broker/Message.h> #include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/FieldValue.h" #include "qpid/sys/Time.h" #include "qpid/sys/Thread.h" #include "qpid/broker/ConnectionState.h" @@ -44,14 +45,15 @@ #include <sstream> #include <typeinfo> +namespace qpid { +namespace management { + using boost::intrusive_ptr; using qpid::framing::Uuid; using qpid::types::Variant; using qpid::amqp_0_10::MapCodec; using qpid::amqp_0_10::ListCodec; -using qpid::sys::Mutex; using namespace qpid::framing; -using namespace qpid::management; using namespace qpid::broker; using namespace qpid; using namespace std; @@ -117,7 +119,7 @@ ManagementAgent::RemoteAgent::~RemoteAgent () } ManagementAgent::ManagementAgent (const bool qmfV1, const bool qmfV2) : - threadPoolSize(1), interval(10), broker(0), timer(0), + threadPoolSize(1), publish(true), interval(10), broker(0), timer(0), startTime(sys::now()), suppressed(false), disallowAllV1Methods(false), vendorNameKey(defaultVendorName), productNameKey(defaultProductName), @@ -164,10 +166,11 @@ ManagementAgent::~ManagementAgent () } } -void ManagementAgent::configure(const string& _dataDir, uint16_t _interval, +void ManagementAgent::configure(const string& _dataDir, bool _publish, uint16_t _interval, qpid::broker::Broker* _broker, int _threads) { dataDir = _dataDir; + publish = _publish; interval = _interval; broker = _broker; threadPoolSize = _threads; @@ -428,16 +431,17 @@ void ManagementAgent::raiseEvent(const ManagementEvent& event, severity_t severi } ManagementAgent::Periodic::Periodic (ManagementAgent& _agent, uint32_t _seconds) - : TimerTask (sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC), - "ManagementAgent::periodicProcessing"), + : TimerTask(sys::Duration((_seconds ? _seconds : 1) * sys::TIME_SEC), + "ManagementAgent::periodicProcessing"), agent(_agent) {} -ManagementAgent::Periodic::~Periodic () {} +ManagementAgent::Periodic::~Periodic() {} -void ManagementAgent::Periodic::fire () +void ManagementAgent::Periodic::fire() { - agent.timer->add (new Periodic (agent, agent.interval)); - agent.periodicProcessing (); + setupNextFire(); + agent.timer->add(this); + agent.periodicProcessing(); } void ManagementAgent::clientAdded (const string& routingKey) @@ -562,7 +566,7 @@ void ManagementAgent::sendBufferLH(Buffer& buf, DeliverableMessage deliverable (msg); try { - exchange->route(deliverable, routingKey, 0); + exchange->route(deliverable); } catch(exception&) {} } buf.reset(); @@ -639,7 +643,7 @@ void ManagementAgent::sendBufferLH(const string& data, DeliverableMessage deliverable (msg); try { - exchange->route(deliverable, routingKey, 0); + exchange->route(deliverable); } catch(exception&) {} } } @@ -719,11 +723,16 @@ void ManagementAgent::periodicProcessing (void) string routingKey; string sBuf; - uint64_t uptime = sys::Duration(startTime, sys::now()); - static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); - moveNewObjectsLH(); - qpid::sys::MemStat::loadMemInfo(memstat); + + // + // If we're publishing updates, get the latest memory statistics and uptime now + // + if (publish) { + uint64_t uptime = sys::Duration(startTime, sys::now()); + static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); + qpid::sys::MemStat::loadMemInfo(memstat); + } // // Clear the been-here flag on all objects in the map. @@ -747,6 +756,14 @@ void ManagementAgent::periodicProcessing (void) // would incorrectly think the object was deleted. See QPID-2997 // bool objectsDeleted = moveDeletedObjectsLH(); + + // + // If we are not publishing updates, just clear the pending deletes. There's no + // need to tell anybody. + // + if (!publish) + pendingDeletedObjs.clear(); + if (!pendingDeletedObjs.empty()) { // use a temporary copy of the pending deletes so dropping the lock when // the buffer is sent is safe. @@ -867,7 +884,9 @@ void ManagementAgent::periodicProcessing (void) // sendBuffer(). This allows the managementObjects map to be altered during the // sendBuffer() call, so always restart the search after a sendBuffer() call // - while (1) { + // If publish is disabled, don't send any updates. + // + while (publish) { msgBuffer.reset(); Variant::List list_; uint32_t pcount; @@ -1023,10 +1042,9 @@ void ManagementAgent::periodicProcessing (void) if (objectsDeleted) deleteOrphanedAgentsLH(); - // heartbeat generation + // heartbeat generation. Note that heartbeats need to be sent even if publish is disabled. if (qmf1Support) { -#define BUFSIZE 65536 uint32_t contentSize; char msgChars[BUFSIZE]; Buffer msgBuffer(msgChars, BUFSIZE); @@ -1087,7 +1105,7 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) Variant::List list_; stringstream v1key, v2key; - if (qmf1Support) { + if (publish && qmf1Support) { string sBuf; v1key << "console.obj.1.0." << object->getPackageName() << "." << object->getClassName(); @@ -1096,7 +1114,7 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) msgBuffer.putRawData(sBuf); } - if (qmf2Support) { + if (publish && qmf2Support) { Variant::Map map_; Variant::Map values; @@ -1121,14 +1139,14 @@ void ManagementAgent::deleteObjectNowLH(const ObjectId& oid) // object deleted, ok to drop lock now. - if (qmf1Support) { + if (publish && qmf1Support) { uint32_t contentSize = msgBuffer.getPosition(); msgBuffer.reset(); sendBufferLH(msgBuffer, contentSize, mExchange, v1key.str()); QPID_LOG(debug, "SEND Immediate(delete) ContentInd to=" << v1key.str()); } - if (qmf2Support) { + if (publish && qmf2Support) { Variant::Map headers; headers["method"] = "indication"; headers["qmf.opcode"] = "_data_indication"; @@ -1841,6 +1859,12 @@ void ManagementAgent::handleGetQueryLH(Buffer& inBuffer, const string& replyToKe if (className == "memory") qpid::sys::MemStat::loadMemInfo(memstat); + if (className == "broker") { + uint64_t uptime = sys::Duration(startTime, sys::now()); + static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); + } + + // build up a set of all objects to be dumped for (ManagementObjectMap::iterator iter = managementObjects.begin(); iter != managementObjects.end(); @@ -1956,6 +1980,11 @@ void ManagementAgent::handleGetQueryLH(const string& body, const string& rte, co if (className == "memory") qpid::sys::MemStat::loadMemInfo(memstat); + if (className == "broker") { + uint64_t uptime = sys::Duration(startTime, sys::now()); + static_cast<_qmf::Broker*>(broker->GetManagementObject())->set_uptime(uptime); + } + /* * Unpack the _object_id element of the query if it is present. If it is present, find that one * object and return it. If it is not present, send a class-based result. @@ -2934,9 +2963,6 @@ bool ManagementAgent::moveDeletedObjectsLH() { return !deleteList.empty(); } -namespace qpid { -namespace management { - namespace { QPID_TSS const qpid::broker::ConnectionState* executionContext = 0; } @@ -2951,3 +2977,4 @@ const qpid::broker::ConnectionState* getManagementExecutionContext() } }} + diff --git a/cpp/src/qpid/management/ManagementAgent.h b/cpp/src/qpid/management/ManagementAgent.h index f68bfe0577..c7e830dcf5 100644 --- a/cpp/src/qpid/management/ManagementAgent.h +++ b/cpp/src/qpid/management/ManagementAgent.h @@ -36,7 +36,6 @@ #include "qpid/sys/MemStat.h" #include "qpid/types/Variant.h" #include <qpid/framing/AMQFrame.h> -#include <qpid/framing/FieldValue.h> #include <qpid/framing/ResizableBuffer.h> #include <memory> #include <string> @@ -72,7 +71,7 @@ public: virtual ~ManagementAgent (); /** Called before plugins are initialized */ - void configure (const std::string& dataDir, uint16_t interval, + void configure (const std::string& dataDir, bool publish, uint16_t interval, qpid::broker::Broker* broker, int threadPoolSize); /** Called after plugins are initialized. */ void pluginsInitialized(); @@ -300,6 +299,7 @@ private: qpid::broker::Exchange::shared_ptr v2Topic; qpid::broker::Exchange::shared_ptr v2Direct; std::string dataDir; + bool publish; uint16_t interval; qpid::broker::Broker* broker; qpid::sys::Timer* timer; diff --git a/cpp/src/qpid/management/ManagementDirectExchange.cpp b/cpp/src/qpid/management/ManagementDirectExchange.cpp index 1d5f8bbd6b..312eacf462 100644 --- a/cpp/src/qpid/management/ManagementDirectExchange.cpp +++ b/cpp/src/qpid/management/ManagementDirectExchange.cpp @@ -40,17 +40,17 @@ ManagementDirectExchange::ManagementDirectExchange(const std::string& _name, DirectExchange(_name, _durable, _args, _parent, b), managementAgent(0) {} -void ManagementDirectExchange::route(Deliverable& msg, - const string& routingKey, - const FieldTable* args) +void ManagementDirectExchange::route(Deliverable& msg) { bool routeIt = true; + const string& routingKey = msg.getMessage().getRoutingKey(); + const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (managementAgent) routeIt = managementAgent->dispatchCommand(msg, routingKey, args, false, qmfVersion); if (routeIt) - DirectExchange::route(msg, routingKey, args); + DirectExchange::route(msg); } void ManagementDirectExchange::setManagmentAgent(ManagementAgent* agent, int qv) diff --git a/cpp/src/qpid/management/ManagementDirectExchange.h b/cpp/src/qpid/management/ManagementDirectExchange.h index 7507179c06..582354d723 100644 --- a/cpp/src/qpid/management/ManagementDirectExchange.h +++ b/cpp/src/qpid/management/ManagementDirectExchange.h @@ -43,9 +43,7 @@ class ManagementDirectExchange : public virtual DirectExchange virtual std::string getType() const { return typeName; } - virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg); void setManagmentAgent(management::ManagementAgent* agent, int qmfVersion); diff --git a/cpp/src/qpid/management/ManagementTopicExchange.cpp b/cpp/src/qpid/management/ManagementTopicExchange.cpp index ee8657646f..587cc660df 100644 --- a/cpp/src/qpid/management/ManagementTopicExchange.cpp +++ b/cpp/src/qpid/management/ManagementTopicExchange.cpp @@ -39,18 +39,18 @@ ManagementTopicExchange::ManagementTopicExchange(const std::string& _name, TopicExchange(_name, _durable, _args, _parent, b), managementAgent(0) {} -void ManagementTopicExchange::route(Deliverable& msg, - const string& routingKey, - const FieldTable* args) +void ManagementTopicExchange::route(Deliverable& msg) { bool routeIt = true; + const string& routingKey = msg.getMessage().getRoutingKey(); + const FieldTable* args = msg.getMessage().getApplicationHeaders(); // Intercept management agent commands if (managementAgent) routeIt = managementAgent->dispatchCommand(msg, routingKey, args, true, qmfVersion); if (routeIt) - TopicExchange::route(msg, routingKey, args); + TopicExchange::route(msg); } bool ManagementTopicExchange::bind(Queue::shared_ptr queue, diff --git a/cpp/src/qpid/management/ManagementTopicExchange.h b/cpp/src/qpid/management/ManagementTopicExchange.h index 232300265e..eff01a8552 100644 --- a/cpp/src/qpid/management/ManagementTopicExchange.h +++ b/cpp/src/qpid/management/ManagementTopicExchange.h @@ -43,9 +43,7 @@ class ManagementTopicExchange : public virtual TopicExchange virtual std::string getType() const { return typeName; } - virtual void route(Deliverable& msg, - const std::string& routingKey, - const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg); virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, diff --git a/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/cpp/src/qpid/replication/ReplicatingEventListener.cpp index 0ced4d9161..9284bda388 100644 --- a/cpp/src/qpid/replication/ReplicatingEventListener.cpp +++ b/cpp/src/qpid/replication/ReplicatingEventListener.cpp @@ -80,7 +80,7 @@ void ReplicatingEventListener::route(boost::intrusive_ptr<qpid::broker::Message> try { if (exchange) { DeliverableMessage deliverable(msg); - exchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders()); + exchange->route(deliverable); } else if (queue) { queue->deliver(msg); } else { @@ -131,7 +131,7 @@ boost::intrusive_ptr<Message> ReplicatingEventListener::cloneMessage(Queue& queu //cloned body: AMQFrame header(*original->getFrames().getHeaders()); header.setBof(false); - header.setEof(!original->getFrames().getContentSize());//if there is any content then the header is not the end of the frameset + header.setEof(!original->getFrames().hasContent());//if there are any content frames then the header is not the end of the frameset header.setBos(true); header.setEos(true); handler.handle(header); diff --git a/cpp/src/qpid/replication/ReplicationExchange.cpp b/cpp/src/qpid/replication/ReplicationExchange.cpp index 89a2bf516d..66f4f14d0c 100644 --- a/cpp/src/qpid/replication/ReplicationExchange.cpp +++ b/cpp/src/qpid/replication/ReplicationExchange.cpp @@ -50,8 +50,9 @@ ReplicationExchange::ReplicationExchange(const std::string& name, bool durable, std::string ReplicationExchange::getType() const { return typeName; } -void ReplicationExchange::route(Deliverable& msg, const std::string& /*routingKey*/, const FieldTable* args) +void ReplicationExchange::route(Deliverable& msg) { + const FieldTable* args = msg.getMessage().getApplicationHeaders(); if (mgmtExchange != 0) { mgmtExchange->inc_msgReceives(); mgmtExchange->inc_byteReceives(msg.contentSize()); @@ -59,7 +60,13 @@ void ReplicationExchange::route(Deliverable& msg, const std::string& /*routingKe if (args) { int eventType = args->getAsInt(REPLICATION_EVENT_TYPE); if (eventType) { - if (isDuplicate(args)) return; + if (isDuplicate(args)) { + if (mgmtExchange != 0) { + mgmtExchange->inc_msgDrops(); + mgmtExchange->inc_byteDrops(msg.contentSize()); + } + return; + } switch (eventType) { case ENQUEUE: handleEnqueueEvent(args, msg); diff --git a/cpp/src/qpid/replication/ReplicationExchange.h b/cpp/src/qpid/replication/ReplicationExchange.h index 4b34e0df13..ff0a98c48e 100644 --- a/cpp/src/qpid/replication/ReplicationExchange.h +++ b/cpp/src/qpid/replication/ReplicationExchange.h @@ -52,7 +52,7 @@ class ReplicationExchange : public qpid::broker::Exchange std::string getType() const; - void route(qpid::broker::Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + void route(qpid::broker::Deliverable& msg); bool bind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args); bool unbind(boost::shared_ptr<broker::Queue> queue, const std::string& routingKey, const qpid::framing::FieldTable* args); diff --git a/cpp/src/qpid/store/MessageStorePlugin.cpp b/cpp/src/qpid/store/MessageStorePlugin.cpp index 2a8d971987..20231bf910 100644 --- a/cpp/src/qpid/store/MessageStorePlugin.cpp +++ b/cpp/src/qpid/store/MessageStorePlugin.cpp @@ -28,6 +28,9 @@ #include "qpid/DataDir.h" #include "qpid/log/Statement.h" +namespace qpid { +namespace store { + /* * The MessageStore pointer given to the Broker points to static storage. * Thus, it cannot be deleted, especially by the broker. To prevent deletion, @@ -42,9 +45,6 @@ namespace { }; } -namespace qpid { -namespace store { - static MessageStorePlugin static_instance_registers_plugin; diff --git a/cpp/src/qpid/store/ms-clfs/MessageLog.cpp b/cpp/src/qpid/store/ms-clfs/MessageLog.cpp index 14d63a4cd4..849a0a44e8 100644 --- a/cpp/src/qpid/store/ms-clfs/MessageLog.cpp +++ b/cpp/src/qpid/store/ms-clfs/MessageLog.cpp @@ -32,6 +32,10 @@ #include "MessageLog.h" #include "Lsn.h" +namespace qpid { +namespace store { +namespace ms_clfs { + namespace { // Structures that hold log records. Each has a type field at the start. @@ -97,10 +101,6 @@ struct MessageDequeue { } // namespace -namespace qpid { -namespace store { -namespace ms_clfs { - void MessageLog::initialize() { diff --git a/cpp/src/qpid/store/ms-clfs/Transaction.h b/cpp/src/qpid/store/ms-clfs/Transaction.h index fd07f2fb2e..499b01d503 100644 --- a/cpp/src/qpid/store/ms-clfs/Transaction.h +++ b/cpp/src/qpid/store/ms-clfs/Transaction.h @@ -27,6 +27,7 @@ #include <boost/enable_shared_from_this.hpp> #include <boost/shared_ptr.hpp> #include <string> +#include <vector> #include "TransactionLog.h" diff --git a/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp b/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp index 04780e83e8..0ef046d7c8 100644 --- a/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp +++ b/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp @@ -33,6 +33,10 @@ #include "Transaction.h" #include "Lsn.h" +namespace qpid { +namespace store { +namespace ms_clfs { + namespace { // Structures that hold log records. Each has a type field at the start. @@ -95,10 +99,6 @@ struct TransactionDelete { } // namespace -namespace qpid { -namespace store { -namespace ms_clfs { - void TransactionLog::initialize() { diff --git a/cpp/src/qpid/sys/apr/APRPool.h b/cpp/src/qpid/sys/MemStat.h index da7661fcfa..d855786cd5 100644 --- a/cpp/src/qpid/sys/apr/APRPool.h +++ b/cpp/src/qpid/sys/MemStat.h @@ -1,6 +1,3 @@ -#ifndef _APRPool_ -#define _APRPool_ - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -10,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 @@ -21,30 +18,21 @@ * under the License. * */ -#include <boost/noncopyable.hpp> -#include <apr_pools.h> +#ifndef sys_MemStat +#define sys_MemStat + +#include "qpid/CommonImportExport.h" +#include "qmf/org/apache/qpid/broker/Memory.h" namespace qpid { namespace sys { -/** - * Singleton APR memory pool. - */ -class APRPool : private boost::noncopyable { - public: - APRPool(); - ~APRPool(); - /** Get singleton instance */ - static apr_pool_t* get(); - - private: - apr_pool_t* pool; -}; + class QPID_COMMON_CLASS_EXTERN MemStat { + public: + QPID_COMMON_EXTERN static void loadMemInfo(qmf::org::apache::qpid::broker::Memory* object); + }; }} +#endif - - - -#endif /*!_APRPool_*/ diff --git a/cpp/src/qpid/sys/Probes.h b/cpp/src/qpid/sys/Probes.h new file mode 100644 index 0000000000..d30181c357 --- /dev/null +++ b/cpp/src/qpid/sys/Probes.h @@ -0,0 +1,65 @@ +#ifndef _sys_Probes +#define _sys_Probes +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "config.h" + +#ifdef HAVE_SYS_SDT_H +#include <sys/sdt.h> +#endif + +// Pragmatically it seems that Linux and Solaris versions of sdt.h which support +// user static probes define up to DTRACE_PROBE8, but FreeBSD 8 which doesn't +// support usdt only defines up to DTRACE_PROBE4 - FreeBSD 9 which does support usdt +// defines up to DTRACE_PROBE5. + +#ifdef DTRACE_PROBE5 +// Versions for Linux Systemtap/Solaris/FreeBSD 9 +#define QPID_PROBE(probe) DTRACE_PROBE(qpid, probe) +#define QPID_PROBE1(probe, p1) DTRACE_PROBE1(qpid, probe, p1) +#define QPID_PROBE2(probe, p1, p2) DTRACE_PROBE2(qpid, probe, p1, p2) +#define QPID_PROBE3(probe, p1, p2, p3) DTRACE_PROBE3(qpid, probe, p1, p2, p3) +#define QPID_PROBE4(probe, p1, p2, p3, p4) DTRACE_PROBE4(qpid, probe, p1, p2, p3, p4) +#define QPID_PROBE5(probe, p1, p2, p3, p4, p5) DTRACE_PROBE5(qpid, probe, p1, p2, p3, p4, p5) +#else +// FreeBSD 8 +#define QPID_PROBE(probe) +#define QPID_PROBE1(probe, p1) +#define QPID_PROBE2(probe, p1, p2) +#define QPID_PROBE3(probe, p1, p2, p3) +#define QPID_PROBE4(probe, p1, p2, p3, p4) +#define QPID_PROBE5(probe, p1, p2, p3, p4, p5) +#endif + +#ifdef DTRACE_PROBE8 +// Versions for Linux Systemtap +#define QPID_PROBE6(probe, p1, p2, p3, p4, p5, p6) DTRACE_PROBE6(qpid, probe, p1, p2, p3, p4, p5, p6) +#define QPID_PROBE7(probe, p1, p2, p3, p4, p5, p6, p7) DTRACE_PROBE7(qpid, probe, p1, p2, p3, p4, p5, p6, p7) +#define QPID_PROBE8(probe, p1, p2, p3, p4, p5, p6, p7, p8) DTRACE_PROBE8(qpid, probe, p1, p2, p3, p4, p5, p6, p7, p8) +#else +// Versions for Solaris/FreeBSD +#define QPID_PROBE6(probe, p1, p2, p3, p4, p5, p6) +#define QPID_PROBE7(probe, p1, p2, p3, p4, p5, p6, p7) +#define QPID_PROBE8(probe, p1, p2, p3, p4, p5, p6, p7, p8) +#endif + +#endif // _sys_Probes diff --git a/cpp/src/qpid/sys/apr/APRBase.cpp b/cpp/src/qpid/sys/apr/APRBase.cpp deleted file mode 100644 index 8bdba66bdc..0000000000 --- a/cpp/src/qpid/sys/apr/APRBase.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include <iostream> -#include "qpid/log/Statement.h" -#include "qpid/sys/apr/APRBase.h" - -using namespace qpid::sys; - -APRBase* APRBase::instance = 0; - -APRBase* APRBase::getInstance(){ - if(instance == 0){ - instance = new APRBase(); - } - return instance; -} - - -APRBase::APRBase() : count(0){ - apr_initialize(); - CHECK_APR_SUCCESS(apr_pool_create(&pool, 0)); - CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); -} - -APRBase::~APRBase(){ - CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); - apr_pool_destroy(pool); - apr_terminate(); -} - -bool APRBase::_increment(){ - bool deleted(false); - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); - if(this == instance){ - count++; - }else{ - deleted = true; - } - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); - return !deleted; -} - -void APRBase::_decrement(){ - APRBase* copy = 0; - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); - if(--count == 0){ - copy = instance; - instance = 0; - } - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); - if(copy != 0){ - delete copy; - } -} - -void APRBase::increment(){ - int count = 0; - while(count++ < 2 && !getInstance()->_increment()) - QPID_LOG(warning, "APR initialization triggered concurrently with termination."); -} - -void APRBase::decrement(){ - getInstance()->_decrement(); -} - -std::string qpid::sys::get_desc(apr_status_t status){ - const int size = 50; - char tmp[size]; - return std::string(apr_strerror(status, tmp, size)); -} - diff --git a/cpp/src/qpid/sys/apr/APRBase.h b/cpp/src/qpid/sys/apr/APRBase.h deleted file mode 100644 index 7b5644a129..0000000000 --- a/cpp/src/qpid/sys/apr/APRBase.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#ifndef _APRBase_ -#define _APRBase_ - -#include <string> -#include <apr_thread_mutex.h> -#include <apr_errno.h> - -namespace qpid { -namespace sys { - - /** - * Use of APR libraries necessitates explicit init and terminate - * calls. Any class using APR libs should obtain the reference to - * this singleton and increment on construction, decrement on - * destruction. This class can then correctly initialise apr - * before the first use and terminate after the last use. - */ - class APRBase{ - static APRBase* instance; - apr_pool_t* pool; - apr_thread_mutex_t* mutex; - int count; - - APRBase(); - ~APRBase(); - static APRBase* getInstance(); - bool _increment(); - void _decrement(); - public: - static void increment(); - static void decrement(); - }; - - //this is also a convenient place for a helper function for error checking: - void check(apr_status_t status, const char* file, const int line); - std::string get_desc(apr_status_t status); - -#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__); - -} -} - -// Inlined as it is called *a lot* -void inline qpid::sys::check(apr_status_t status, const char* file, const int line){ - if (status != APR_SUCCESS){ - char tmp[256]; - throw Exception(QPID_MSG(apr_strerror(status, tmp, size))) - } -} - - - - -#endif diff --git a/cpp/src/qpid/sys/apr/APRPool.cpp b/cpp/src/qpid/sys/apr/APRPool.cpp deleted file mode 100644 index e221bfc2f1..0000000000 --- a/cpp/src/qpid/sys/apr/APRPool.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/sys/apr/APRPool.h" -#include "qpid/sys/apr/APRBase.h" -#include <boost/pool/detail/singleton.hpp> - -using namespace qpid::sys; - -APRPool::APRPool(){ - APRBase::increment(); - CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); -} - -APRPool::~APRPool(){ - apr_pool_destroy(pool); - APRBase::decrement(); -} - -apr_pool_t* APRPool::get() { - return boost::details::pool::singleton_default<APRPool>::instance().pool; -} - diff --git a/cpp/src/qpid/sys/apr/Condition.h b/cpp/src/qpid/sys/apr/Condition.h deleted file mode 100644 index 66d465ca75..0000000000 --- a/cpp/src/qpid/sys/apr/Condition.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef _sys_apr_Condition_h -#define _sys_apr_Condition_h - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/apr/APRPool.h" -#include "qpid/sys/Mutex.h" -#include "qpid/sys/Time.h" - -#include <sys/errno.h> -#include <boost/noncopyable.hpp> -#include <apr_thread_cond.h> - -namespace qpid { -namespace sys { - -/** - * A condition variable for thread synchronization. - */ -class Condition -{ - public: - inline Condition(); - inline ~Condition(); - inline void wait(Mutex&); - inline bool wait(Mutex&, const AbsTime& absoluteTime); - inline void notify(); - inline void notifyAll(); - - private: - apr_thread_cond_t* condition; -}; - - -Condition::Condition() { - CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get())); -} - -Condition::~Condition() { - CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); -} - -void Condition::wait(Mutex& mutex) { - CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex)); -} - -bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ - // APR uses microseconds. - apr_status_t status = - apr_thread_cond_timedwait( - condition, mutex.mutex, Duration(now(), absoluteTime)/TIME_USEC); - if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status); - return status == 0; -} - -void Condition::notify(){ - CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); -} - -void Condition::notifyAll(){ - CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); -} - -}} -#endif /*!_sys_apr_Condition_h*/ diff --git a/cpp/src/qpid/sys/apr/Mutex.h b/cpp/src/qpid/sys/apr/Mutex.h deleted file mode 100644 index cb75f5b339..0000000000 --- a/cpp/src/qpid/sys/apr/Mutex.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef _sys_apr_Mutex_h -#define _sys_apr_Mutex_h - -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "qpid/sys/apr/APRBase.h" -#include "qpid/sys/apr/APRPool.h" - -#include <boost/noncopyable.hpp> -#include <apr_thread_mutex.h> - -namespace qpid { -namespace sys { - -class Condition; - -/** - * Mutex lock. - */ -class Mutex : private boost::noncopyable { - public: - typedef ScopedLock<Mutex> ScopedLock; - typedef ScopedUnlock<Mutex> ScopedUnlock; - - inline Mutex(); - inline ~Mutex(); - inline void lock(); - inline void unlock(); - inline bool trylock(); - - protected: - apr_thread_mutex_t* mutex; - friend class Condition; -}; - -Mutex::Mutex() { - CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); -} - -Mutex::~Mutex(){ - CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); -} - -void Mutex::lock() { - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); -} -void Mutex::unlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); -} - -bool Mutex::trylock() { - return apr_thread_mutex_trylock(mutex) == 0; -} - - -/** - * RW lock. - */ -class RWlock : private boost::noncopyable { - friend class Condition; - -public: - typedef ScopedRlock<RWlock> ScopedRlock; - typedef ScopedWlock<RWlock> ScopedWlock; - - inline RWlock(); - inline ~RWlock(); - inline void wlock(); // will write-lock - inline void rlock(); // will read-lock - inline void unlock(); - inline bool trywlock(); // will write-try - inline bool tryrlock(); // will read-try - - protected: - apr_thread_mutex_t* mutex; -}; - -RWlock::RWlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); -} - -RWlock::~RWlock(){ - CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); -} - -void RWlock::wlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); -} - -void RWlock::rlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); -} - -void RWlock::unlock() { - CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); -} - -bool RWlock::trywlock() { - return apr_thread_mutex_trylock(mutex) == 0; -} - -bool RWlock::tryrlock() { - return apr_thread_mutex_trylock(mutex) == 0; -} - - -}} -#endif /*!_sys_apr_Mutex_h*/ diff --git a/cpp/src/qpid/sys/apr/Shlib.cpp b/cpp/src/qpid/sys/apr/Shlib.cpp deleted file mode 100644 index b7ee13a03b..0000000000 --- a/cpp/src/qpid/sys/apr/Shlib.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/Shlib.h" -#include "qpid/sys/apr/APRBase.h" -#include "qpid/sys/apr/APRPool.h" -#include <apr_dso.h> - -namespace qpid { -namespace sys { - -void Shlib::load(const char* libname) { - apr_dso_handle_t* aprHandle; - CHECK_APR_SUCCESS( - apr_dso_load(&aprHandle, libname, APRPool::get())); - handle=aprHandle; -} - -void Shlib::unload() { - CHECK_APR_SUCCESS( - apr_dso_unload(static_cast<apr_dso_handle_t*>(handle))); -} - -void* Shlib::getSymbol(const char* name) { - apr_dso_handle_sym_t symbol; - CHECK_APR_SUCCESS(apr_dso_sym(&symbol, - static_cast<apr_dso_handle_t*>(handle), - name)); - return (void*) symbol; -} - -}} // namespace qpid::sys diff --git a/cpp/src/qpid/sys/apr/Socket.cpp b/cpp/src/qpid/sys/apr/Socket.cpp deleted file mode 100644 index d9024d11c1..0000000000 --- a/cpp/src/qpid/sys/apr/Socket.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - - -#include "qpid/sys/Socket.h" - -#include "qpid/sys/apr/APRBase.h" -#include "qpid/sys/apr/APRPool.h" - -#include <apr_network_io.h> - -namespace qpid { -namespace sys { - -class SocketPrivate { -public: - SocketPrivate(apr_socket_t* s = 0) : - socket(s) - {} - - apr_socket_t* socket; -}; - -Socket::Socket() : - impl(new SocketPrivate) -{ - createTcp(); -} - -Socket::Socket(SocketPrivate* sp) : - impl(sp) -{} - -Socket::~Socket() { - delete impl; -} - -void Socket::createTcp() const { - apr_socket_t*& socket = impl->socket; - apr_socket_t* s; - CHECK_APR_SUCCESS( - apr_socket_create( - &s, APR_INET, SOCK_STREAM, APR_PROTO_TCP, - APRPool::get())); - socket = s; -} - -void Socket::setTimeout(const Duration& interval) const { - apr_socket_t*& socket = impl->socket; - apr_socket_timeout_set(socket, interval/TIME_USEC); -} - -void Socket::connect(const std::string& host, int port) const { - apr_socket_t*& socket = impl->socket; - apr_sockaddr_t* address; - CHECK_APR_SUCCESS( - apr_sockaddr_info_get( - &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, - APRPool::get())); - CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); -} - -void Socket::close() const { - apr_socket_t*& socket = impl->socket; - if (socket == 0) return; - CHECK_APR_SUCCESS(apr_socket_close(socket)); - socket = 0; -} - -ssize_t Socket::send(const void* data, size_t size) const -{ - apr_socket_t*& socket = impl->socket; - apr_size_t sent = size; - apr_status_t status = - apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent); - if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT; - if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF; - CHECK_APR_SUCCESS(status); - return sent; -} - -ssize_t Socket::recv(void* data, size_t size) const -{ - apr_socket_t*& socket = impl->socket; - apr_size_t received = size; - apr_status_t status = - apr_socket_recv(socket, reinterpret_cast<char*>(data), &received); - if (APR_STATUS_IS_TIMEUP(status)) - return SOCKET_TIMEOUT; - if (APR_STATUS_IS_EOF(status)) - return SOCKET_EOF; - CHECK_APR_SUCCESS(status); - return received; -} - -}} // namespace qpid::sys diff --git a/cpp/src/qpid/sys/apr/Thread.cpp b/cpp/src/qpid/sys/apr/Thread.cpp deleted file mode 100644 index b52d0e6ace..0000000000 --- a/cpp/src/qpid/sys/apr/Thread.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/apr/Thread.h" -#include "qpid/sys/Runnable.h" - -using namespace qpid::sys; -using qpid::sys::Runnable; - -void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) { - reinterpret_cast<Runnable*>(data)->run(); - CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); - return NULL; -} - - diff --git a/cpp/src/qpid/sys/apr/Thread.h b/cpp/src/qpid/sys/apr/Thread.h deleted file mode 100644 index 6cc63db5c9..0000000000 --- a/cpp/src/qpid/sys/apr/Thread.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef _sys_apr_Thread_h -#define _sys_apr_Thread_h - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include "qpid/sys/apr/APRPool.h" -#include "qpid/sys/apr/APRBase.h" - -#include <apr_thread_proc.h> -#include <apr_portable.h> - -namespace qpid { -namespace sys { - -class Runnable; - -class Thread -{ - public: - inline static Thread current(); - - /** ID of current thread for logging. - * Workaround for broken Thread::current() in APR - */ - inline static long logId(); - - inline static void yield(); - - inline Thread(); - inline explicit Thread(qpid::sys::Runnable*); - inline explicit Thread(qpid::sys::Runnable&); - - inline void join(); - - inline long id(); - - private: - static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data); - inline Thread(apr_thread_t* t); - apr_thread_t* thread; -}; - -Thread::Thread() : thread(0) {} - -Thread::Thread(Runnable* runnable) { - CHECK_APR_SUCCESS( - apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get())); -} - -Thread::Thread(Runnable& runnable) { - CHECK_APR_SUCCESS( - apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get())); -} - -void Thread::join(){ - apr_status_t status; - if (thread != 0) - CHECK_APR_SUCCESS(apr_thread_join(&status, thread)); -} - -long Thread::id() { - return long(thread); -} - -/** ID of current thread for logging. - * Workaround for broken Thread::current() in APR - */ -long Thread::logId() { - return static_cast<long>(apr_os_thread_current()); -} - -Thread::Thread(apr_thread_t* t) : thread(t) {} - -Thread Thread::current(){ - apr_thread_t* thr; - apr_os_thread_t osthr = apr_os_thread_current(); - CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get())); - return Thread(thr); -} - -void Thread::yield() -{ - apr_thread_yield(); -} - -}} -#endif /*!_sys_apr_Thread_h*/ diff --git a/cpp/src/qpid/sys/posix/AsynchIO.cpp b/cpp/src/qpid/sys/posix/AsynchIO.cpp index a1c161b596..01ff8b6bfa 100644 --- a/cpp/src/qpid/sys/posix/AsynchIO.cpp +++ b/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -23,6 +23,7 @@ #include "qpid/sys/Socket.h" #include "qpid/sys/SocketAddress.h" #include "qpid/sys/Poller.h" +#include "qpid/sys/Probes.h" #include "qpid/sys/DispatchHandle.h" #include "qpid/sys/Time.h" #include "qpid/log/Statement.h" @@ -40,7 +41,9 @@ #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> -using namespace qpid::sys; +namespace qpid { +namespace sys { +namespace posix { namespace { @@ -70,10 +73,6 @@ __thread int64_t threadMaxIoTimeNs = 2 * 1000000; // start at 2ms /* * Asynch Acceptor */ -namespace qpid { -namespace sys { -namespace posix { - class AsynchAcceptor : public qpid::sys::AsynchAcceptor { public: AsynchAcceptor(const Socket& s, AsynchAcceptor::Callback callback); @@ -423,9 +422,12 @@ AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { void AsynchIO::readable(DispatchHandle& h) { if (readingStopped) { // We have been flow controlled. + QPID_PROBE1(asynchio_read_flowcontrolled, &h); return; } AbsTime readStartTime = AbsTime::now(); + size_t total = 0; + int readCalls = 0; do { // (Try to) get a buffer if (!bufferQueue.empty()) { @@ -436,23 +438,29 @@ void AsynchIO::readable(DispatchHandle& h) { errno = 0; int readCount = buff->byteCount-buff->dataCount; int rc = socket.read(buff->bytes + buff->dataCount, readCount); + int64_t duration = Duration(readStartTime, AbsTime::now()); + ++readCalls; if (rc > 0) { buff->dataCount += rc; threadReadTotal += rc; + total += rc; readCallback(*this, buff); if (readingStopped) { // We have been flow controlled. + QPID_PROBE4(asynchio_read_finished_flowcontrolled, &h, duration, total, readCalls); break; } if (rc != readCount) { // If we didn't fill the read buffer then time to stop reading + QPID_PROBE4(asynchio_read_finished_done, &h, duration, total, readCalls); break; } // Stop reading if we've overrun our timeslot - if (Duration(readStartTime, AbsTime::now()) > threadMaxIoTimeNs) { + if ( duration > threadMaxIoTimeNs) { + QPID_PROBE4(asynchio_read_finished_maxtime, &h, duration, total, readCalls); break; } @@ -461,6 +469,7 @@ void AsynchIO::readable(DispatchHandle& h) { bufferQueue.push_front(buff); assert(buff); + QPID_PROBE5(asynchio_read_finished_error, &h, duration, total, readCalls, errno); // Eof or other side has gone away if (rc == 0 || errno == ECONNRESET) { eofCallback(*this); @@ -486,6 +495,7 @@ void AsynchIO::readable(DispatchHandle& h) { // If we still have no buffers we can't do anything more if (bufferQueue.empty()) { h.unwatchRead(); + QPID_PROBE4(asynchio_read_finished_nobuffers, &h, Duration(readStartTime, AbsTime::now()), total, readCalls); break; } @@ -501,6 +511,8 @@ void AsynchIO::readable(DispatchHandle& h) { */ void AsynchIO::writeable(DispatchHandle& h) { AbsTime writeStartTime = AbsTime::now(); + size_t total = 0; + int writeCalls = 0; do { // See if we've got something to write if (!writeQueue.empty()) { @@ -510,14 +522,18 @@ void AsynchIO::writeable(DispatchHandle& h) { errno = 0; assert(buff->dataStart+buff->dataCount <= buff->byteCount); int rc = socket.write(buff->bytes+buff->dataStart, buff->dataCount); + int64_t duration = Duration(writeStartTime, AbsTime::now()); + ++writeCalls; if (rc >= 0) { threadWriteTotal += rc; + total += rc; // If we didn't write full buffer put rest back if (rc != buff->dataCount) { buff->dataStart += rc; buff->dataCount -= rc; writeQueue.push_back(buff); + QPID_PROBE4(asynchio_write_finished_done, &h, duration, total, writeCalls); break; } @@ -525,12 +541,15 @@ void AsynchIO::writeable(DispatchHandle& h) { queueReadBuffer(buff); // Stop writing if we've overrun our timeslot - if (Duration(writeStartTime, AbsTime::now()) > threadMaxIoTimeNs) { + if (duration > threadMaxIoTimeNs) { + QPID_PROBE4(asynchio_write_finished_maxtime, &h, duration, total, writeCalls); break; } } else { // Put buffer back writeQueue.push_back(buff); + QPID_PROBE5(asynchio_write_finished_error, &h, duration, total, writeCalls, errno); + if (errno == ECONNRESET || errno == EPIPE) { // Just stop watching for write here - we'll get a // disconnect callback soon enough @@ -548,9 +567,13 @@ void AsynchIO::writeable(DispatchHandle& h) { } } } else { + int64_t duration = Duration(writeStartTime, AbsTime::now()); + (void) duration; // force duration to be used if no probes are compiled + // If we're waiting to close the socket then can do it now as there is nothing to write if (queuedClose) { close(h); + QPID_PROBE4(asynchio_write_finished_closed, &h, duration, total, writeCalls); break; } // Fd is writable, but nothing to write @@ -567,6 +590,7 @@ void AsynchIO::writeable(DispatchHandle& h) { // desired rewatchWrite so we correct that here if (writePending) h.rewatchWrite(); + QPID_PROBE4(asynchio_write_finished_nodata, &h, duration, total, writeCalls); break; } } diff --git a/cpp/src/qpid/sys/posix/PollableCondition.cpp b/cpp/src/qpid/sys/posix/PollableCondition.cpp index b22a615a54..abff8a5be8 100644 --- a/cpp/src/qpid/sys/posix/PollableCondition.cpp +++ b/cpp/src/qpid/sys/posix/PollableCondition.cpp @@ -1,6 +1,3 @@ -#ifndef QPID_SYS_LINUX_POLLABLECONDITION_CPP -#define QPID_SYS_LINUX_POLLABLECONDITION_CPP - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -120,5 +117,3 @@ void PollableCondition::set() { impl->set(); } void PollableCondition::clear() { impl->clear(); } }} // namespace qpid::sys - -#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/ diff --git a/cpp/src/qpid/sys/ssl/SslIo.cpp b/cpp/src/qpid/sys/ssl/SslIo.cpp index 73f15617dc..2a7cf16923 100644 --- a/cpp/src/qpid/sys/ssl/SslIo.cpp +++ b/cpp/src/qpid/sys/ssl/SslIo.cpp @@ -37,8 +37,9 @@ #include <boost/bind.hpp> -using namespace qpid::sys; -using namespace qpid::sys::ssl; +namespace qpid { +namespace sys { +namespace ssl { namespace { @@ -448,3 +449,5 @@ SecuritySettings SslIO::getSecuritySettings() { settings.authid = socket.getClientAuthId(); return settings; } + +}}} diff --git a/cpp/src/qpid/sys/windows/AsynchIO.cpp b/cpp/src/qpid/sys/windows/AsynchIO.cpp index 30378d4c5f..ae53414e52 100644 --- a/cpp/src/qpid/sys/windows/AsynchIO.cpp +++ b/cpp/src/qpid/sys/windows/AsynchIO.cpp @@ -291,6 +291,8 @@ private: volatile LONG opsInProgress; // Is there a write in progress? volatile bool writeInProgress; + // Or a read? + volatile bool readInProgress; // Deletion requested, but there are callbacks in progress. volatile bool queuedDelete; // Socket close requested, but there are operations in progress. @@ -344,6 +346,11 @@ private: * Called when there's a completion to process. */ void completion(AsynchIoResult *result); + + /** + * Helper function to facilitate the close operation + */ + void cancelRead(); }; // This is used to encapsulate pure callbacks into a handle @@ -372,6 +379,7 @@ AsynchIO::AsynchIO(const Socket& s, socket(s), opsInProgress(0), writeInProgress(false), + readInProgress(false), queuedDelete(false), queuedClose(false), working(false) { @@ -389,21 +397,24 @@ AsynchIO::~AsynchIO() { } void AsynchIO::queueForDeletion() { - queuedDelete = true; - if (opsInProgress > 0) { - QPID_LOG(info, "Delete AsynchIO queued; ops in progress"); - // AsynchIOHandler calls this then deletes itself; don't do any more - // callbacks. - readCallback = 0; - eofCallback = 0; - disCallback = 0; - closedCallback = 0; - emptyCallback = 0; - idleCallback = 0; - } - else { - delete this; + { + ScopedLock<Mutex> l(completionLock); + assert(!queuedDelete); + queuedDelete = true; + if (working || opsInProgress > 0) { + QPID_LOG(info, "Delete AsynchIO queued; ops in progress"); + // AsynchIOHandler calls this then deletes itself; don't do any more + // callbacks. + readCallback = 0; + eofCallback = 0; + disCallback = 0; + closedCallback = 0; + emptyCallback = 0; + idleCallback = 0; + return; + } } + delete this; } void AsynchIO::start(Poller::shared_ptr poller0) { @@ -451,9 +462,14 @@ void AsynchIO::notifyPendingWrite() { } void AsynchIO::queueWriteClose() { - queuedClose = true; - if (!writeInProgress) - notifyPendingWrite(); + { + ScopedLock<Mutex> l(completionLock); + queuedClose = true; + if (working || writeInProgress) + // no need to summon an IO thread + return; + } + notifyPendingWrite(); } bool AsynchIO::writeQueueEmpty() { @@ -466,7 +482,7 @@ bool AsynchIO::writeQueueEmpty() { * called when the read is complete and data is available. */ void AsynchIO::startReading() { - if (queuedDelete) + if (queuedDelete || queuedClose) return; // (Try to) get a buffer; look on the front since there may be an @@ -489,6 +505,7 @@ void AsynchIO::startReading() { readCount); DWORD bytesReceived = 0, flags = 0; InterlockedIncrement(&opsInProgress); + readInProgress = true; int status = WSARecv(toSocketHandle(socket), const_cast<LPWSABUF>(result->getWSABUF()), 1, &bytesReceived, @@ -616,17 +633,19 @@ void AsynchIO::close(void) { void AsynchIO::readComplete(AsynchReadResult *result) { int status = result->getStatus(); size_t bytes = result->getTransferred(); + readInProgress = false; if (status == 0 && bytes > 0) { - bool restartRead = true; // May not if receiver doesn't want more if (readCallback) readCallback(*this, result->getBuff()); - if (restartRead) - startReading(); + startReading(); } else { // No data read, so put the buffer back. It may be partially filled, // so "unread" it back to the front of the queue. unread(result->getBuff()); + if (queuedClose) { + return; // Expected from cancelRead() + } notifyEof(); if (status != 0) { @@ -682,6 +701,8 @@ void AsynchIO::writeComplete(AsynchWriteResult *result) { } void AsynchIO::completion(AsynchIoResult *result) { + bool closing = false; + bool deleting = false; { ScopedLock<Mutex> l(completionLock); if (working) { @@ -713,6 +734,8 @@ void AsynchIO::completion(AsynchIoResult *result) { delete result; result = 0; InterlockedDecrement(&opsInProgress); + if (queuedClose && opsInProgress == 1 && readInProgress) + cancelRead(); } // Lock is held again. if (completionQueue.empty()) @@ -721,17 +744,40 @@ void AsynchIO::completion(AsynchIoResult *result) { completionQueue.pop(); } working = false; + if (opsInProgress == 0) { + closing = queuedClose; + deleting = queuedDelete; + } } // Lock released; ok to close if ops are done and close requested. // Layer above will call back to queueForDeletion() if it hasn't // already been done. If it already has, go ahead and delete. - if (opsInProgress == 0) { - if (queuedClose) - // close() may cause a delete; don't trust 'this' on return - close(); - else if (queuedDelete) - delete this; + if (deleting) + delete this; + else if (closing) + // close() may cause a delete; don't trust 'this' on return + close(); +} + +/* + * NOTE - this method must be called in the same context as other completions, + * so that the resulting readComplete, and final AsynchIO::close() is serialized + * after this method returns. + */ +void AsynchIO::cancelRead() { + if (queuedDelete) + return; // socket already deleted + else { + ScopedLock<Mutex> l(completionLock);; + if (!completionQueue.empty()) + return; // process it; come back later if necessary } + // Cancel outstanding read and force to completion. Otherwise, on a faulty + // physical link, the pending read can remain uncompleted indefinitely. + // Draining the pending read will result in the official close (and + // notifyClosed). CancelIoEX() is the natural choice, but not available in + // XP, so we make do with closesocket(). + socket.close(); } } // namespace windows diff --git a/cpp/src/qpid/sys/windows/PollableCondition.cpp b/cpp/src/qpid/sys/windows/PollableCondition.cpp index 6a1d9045b4..bb637be0a6 100644 --- a/cpp/src/qpid/sys/windows/PollableCondition.cpp +++ b/cpp/src/qpid/sys/windows/PollableCondition.cpp @@ -1,6 +1,3 @@ -#ifndef QPID_SYS_WINDOWS_POLLABLECONDITION_CPP -#define QPID_SYS_WINDOWS_POLLABLECONDITION_CPP - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -110,5 +107,3 @@ void PollableCondition::clear() { } }} // namespace qpid::sys - -#endif /*!QPID_SYS_WINDOWS_POLLABLECONDITION_CPP*/ diff --git a/cpp/src/qpid/sys/windows/Socket.cpp b/cpp/src/qpid/sys/windows/Socket.cpp index 1fa4768329..b085f67539 100644 --- a/cpp/src/qpid/sys/windows/Socket.cpp +++ b/cpp/src/qpid/sys/windows/Socket.cpp @@ -32,6 +32,9 @@ #include <winsock2.h> +namespace qpid { +namespace sys { + // Need to initialize WinSock. Ideally, this would be a singleton or embedded // in some one-time initialization function. I tried boost singleton and could // not get it to compile (and others located in google had the same problem). @@ -76,13 +79,6 @@ protected: static WinSockSetup setup; -} /* namespace */ - -namespace qpid { -namespace sys { - -namespace { - std::string getName(SOCKET fd, bool local) { ::sockaddr_storage name_s; // big enough for any socket address diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp index 11a3389e45..25cc94b290 100644 --- a/cpp/src/qpid/sys/windows/SslAsynchIO.cpp +++ b/cpp/src/qpid/sys/windows/SslAsynchIO.cpp @@ -38,6 +38,10 @@ #include <queue> #include <boost/bind.hpp> +namespace qpid { +namespace sys { +namespace windows { + namespace { /* @@ -66,10 +70,6 @@ namespace { }; } -namespace qpid { -namespace sys { -namespace windows { - SslAsynchIO::SslAsynchIO(const qpid::sys::Socket& s, CredHandle hCred, ReadCallback rCb, diff --git a/cpp/src/qpid/xml/XmlExchange.cpp b/cpp/src/qpid/xml/XmlExchange.cpp index b7ff5d211d..01770e22a6 100644 --- a/cpp/src/qpid/xml/XmlExchange.cpp +++ b/cpp/src/qpid/xml/XmlExchange.cpp @@ -283,8 +283,10 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F // But for very large messages, if all these queries are on the first part of the data, // it could still be a big win. -void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* args) +void XmlExchange::route(Deliverable& msg) { + const string& routingKey = msg.getMessage().getRoutingKey(); + const FieldTable* args = msg.getMessage().getApplicationHeaders(); PreRoute pr(msg, this); try { XmlBinding::vector::ConstPtr p; diff --git a/cpp/src/qpid/xml/XmlExchange.h b/cpp/src/qpid/xml/XmlExchange.h index 958bad4931..9ef389d9bf 100644 --- a/cpp/src/qpid/xml/XmlExchange.h +++ b/cpp/src/qpid/xml/XmlExchange.h @@ -82,7 +82,7 @@ class XmlExchange : public virtual Exchange { virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + virtual void route(Deliverable& msg); virtual bool isBound(Queue::shared_ptr queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args); |