diff options
| author | Rafael H. Schloming <rhs@apache.org> | 2009-12-26 12:42:57 +0000 |
|---|---|---|
| committer | Rafael H. Schloming <rhs@apache.org> | 2009-12-26 12:42:57 +0000 |
| commit | 248f1fe188fe2307b9dcf2c87a83b653eaa1920c (patch) | |
| tree | d5d0959a70218946ff72e107a6c106e32479a398 /cpp/src/qpid/broker | |
| parent | 3c83a0e3ec7cf4dc23e83a340b25f5fc1676f937 (diff) | |
| download | qpid-python-248f1fe188fe2307b9dcf2c87a83b653eaa1920c.tar.gz | |
synchronized with trunk except for ruby dir
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/qpid.rnr@893970 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp/src/qpid/broker')
143 files changed, 7964 insertions, 3868 deletions
diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h index f766978d18..2f4f7eaacc 100644 --- a/cpp/src/qpid/broker/AclModule.h +++ b/cpp/src/qpid/broker/AclModule.h @@ -21,20 +21,30 @@ */ - -#include "qpid/shared_ptr.h" #include "qpid/RefCounted.h" +#include <boost/shared_ptr.hpp> #include <map> +#include <set> #include <string> - +#include <sstream> namespace qpid { -namespace acl{ -enum ObjectType {QUEUE,EXCHANGE,BROKER,LINK,ROUTE}; -enum Action {CONSUME,PUBLISH,CREATE,ACCESS,BIND,UNBIND,DELETE,PURGE,UPDATE}; -enum AclResult {ALLOW,ALLOWLOG,DENY,DENYNOLOG}; -} +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}; + +} // namespace acl namespace broker { @@ -47,17 +57,225 @@ public: // effienty turn off ACL on message transfer. virtual bool doTransferAcl()=0; - virtual bool authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string name, - std::map<std::string, std::string>* params)=0; - virtual bool authorise(std::string id, acl::Action action, acl::ObjectType objType, std::string ExchangeName, - std::string RoutingKey)=0; + 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 ~AclModule() {}; }; +} // namespace broker + +namespace acl { + +class AclHelper { + private: + AclHelper(){} + public: + static inline ObjectType getObjectType(const std::string& str) { + if (str.compare("queue") == 0) return OBJ_QUEUE; + if (str.compare("exchange") == 0) return OBJ_EXCHANGE; + 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; + } + static inline std::string getObjectTypeStr(const ObjectType o) { + switch (o) { + case OBJ_QUEUE: return "queue"; + case OBJ_EXCHANGE: return "exchange"; + case OBJ_BROKER: return "broker"; + case OBJ_LINK: return "link"; + case OBJ_METHOD: return "method"; + default: assert(false); // should never get here + } + return ""; + } + static inline Action getAction(const std::string& str) { + if (str.compare("consume") == 0) return ACT_CONSUME; + if (str.compare("publish") == 0) return ACT_PUBLISH; + if (str.compare("create") == 0) return ACT_CREATE; + if (str.compare("access") == 0) return ACT_ACCESS; + if (str.compare("bind") == 0) return ACT_BIND; + if (str.compare("unbind") == 0) return ACT_UNBIND; + 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; + } + static inline std::string getActionStr(const Action a) { + switch (a) { + case ACT_CONSUME: return "consume"; + case ACT_PUBLISH: return "publish"; + case ACT_CREATE: return "create"; + case ACT_ACCESS: return "access"; + case ACT_BIND: return "bind"; + case ACT_UNBIND: return "unbind"; + case ACT_DELETE: return "delete"; + case ACT_PURGE: return "purge"; + case ACT_UPDATE: return "update"; + default: assert(false); // should never get here + } + return ""; + } + static inline Property getProperty(const std::string& str) { + if (str.compare("name") == 0) return PROP_NAME; + 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; + if (str.compare("alternate") == 0) return PROP_ALTERNATE; + if (str.compare("queuename") == 0) return PROP_QUEUENAME; + if (str.compare("schemapackage") == 0) return PROP_SCHEMAPACKAGE; + 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; + } + static inline std::string getPropertyStr(const Property p) { + switch (p) { + case PROP_NAME: return "name"; + 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"; + case PROP_ALTERNATE: return "alternate"; + case PROP_QUEUENAME: return "queuename"; + case PROP_SCHEMAPACKAGE: return "schemapackage"; + case PROP_SCHEMACLASS: return "schemaclass"; + case PROP_POLICYTYPE: return "policytype"; + case PROP_MAXQUEUESIZE: return "maxqueuesize"; + case PROP_MAXQUEUECOUNT: return "maxqueuecount"; + 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; + } + static inline std::string getAclResultStr(const AclResult r) { + switch (r) { + case ALLOW: return "allow"; + case ALLOWLOG: return "allow-log"; + case DENY: return "deny"; + case DENYLOG: return "deny-log"; + default: assert(false); // should never get here + } + return ""; + } + + typedef std::set<Property> propSet; + typedef boost::shared_ptr<propSet> propSetPtr; + typedef std::pair<Action, propSetPtr> actionPair; + typedef std::map<Action, propSetPtr> actionMap; + typedef boost::shared_ptr<actionMap> actionMapPtr; + typedef std::pair<ObjectType, actionMapPtr> objectPair; + typedef std::map<ObjectType, actionMapPtr> objectMap; + typedef objectMap::const_iterator omCitr; + typedef boost::shared_ptr<objectMap> objectMapPtr; + typedef std::map<Property, std::string> propMap; + typedef propMap::const_iterator propMapItr; + + // 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(); + propSetPtr p0; // empty ptr, used for no properties + + // == Exchanges == + + propSetPtr p1(new propSet); + p1->insert(PROP_TYPE); + p1->insert(PROP_ALTERNATE); + p1->insert(PROP_PASSIVE); + p1->insert(PROP_DURABLE); + + propSetPtr p2(new propSet); + p2->insert(PROP_ROUTINGKEY); + + propSetPtr p3(new propSet); + p3->insert(PROP_QUEUENAME); + p3->insert(PROP_ROUTINGKEY); + + actionMapPtr a0(new actionMap); + a0->insert(actionPair(ACT_CREATE, p1)); + a0->insert(actionPair(ACT_DELETE, p0)); + a0->insert(actionPair(ACT_ACCESS, p0)); + a0->insert(actionPair(ACT_BIND, p2)); + a0->insert(actionPair(ACT_UNBIND, p2)); + a0->insert(actionPair(ACT_ACCESS, p3)); + a0->insert(actionPair(ACT_PUBLISH, p0)); + + map->insert(objectPair(OBJ_EXCHANGE, a0)); + + // == Queues == + + propSetPtr p4(new propSet); + p4->insert(PROP_ALTERNATE); + p4->insert(PROP_PASSIVE); + p4->insert(PROP_DURABLE); + p4->insert(PROP_EXCLUSIVE); + p4->insert(PROP_AUTODELETE); + p4->insert(PROP_POLICYTYPE); + p4->insert(PROP_MAXQUEUESIZE); + p4->insert(PROP_MAXQUEUECOUNT); + + actionMapPtr a1(new actionMap); + a1->insert(actionPair(ACT_ACCESS, p0)); + a1->insert(actionPair(ACT_CREATE, p4)); + a1->insert(actionPair(ACT_PURGE, p0)); + a1->insert(actionPair(ACT_DELETE, p0)); + a1->insert(actionPair(ACT_CONSUME, p0)); + + map->insert(objectPair(OBJ_QUEUE, a1)); + + // == Links == + + actionMapPtr a2(new actionMap); + a2->insert(actionPair(ACT_CREATE, p0)); + + map->insert(objectPair(OBJ_LINK, a2)); + + // == Method == + + propSetPtr p5(new propSet); + p5->insert(PROP_SCHEMAPACKAGE); + p5->insert(PROP_SCHEMACLASS); + + actionMapPtr a4(new actionMap); + a4->insert(actionPair(ACT_ACCESS, p5)); + + map->insert(objectPair(OBJ_METHOD, a4)); + } + + static std::string propertyMapToString(const std::map<Property, 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; + } + } + ss << " }"; + return ss.str(); + } +}; -}} // namespace qpid::broker +}} // 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 53bed020e2..79e311d032 100644 --- a/cpp/src/qpid/broker/Bridge.cpp +++ b/cpp/src/qpid/broker/Bridge.cpp @@ -18,36 +18,62 @@ * under the License. * */ -#include "Bridge.h" -#include "ConnectionState.h" -#include "LinkRegistry.h" +#include "qpid/broker/Bridge.h" +#include "qpid/broker/ConnectionState.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/LinkRegistry.h" +#include "qpid/broker/SessionState.h" -#include "qpid/agent/ManagementAgent.h" -#include "qpid/framing/FieldTable.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/framing/Uuid.h" #include "qpid/log/Statement.h" +#include <iostream> using qpid::framing::FieldTable; using qpid::framing::Uuid; using qpid::framing::Buffer; using qpid::management::ManagementAgent; +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} namespace qpid { namespace broker { +void Bridge::PushHandler::handle(framing::AMQFrame& frame) +{ + conn->received(frame); +} + Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l, - const management::ArgsLinkBridge& _args) : + const _qmf::ArgsLinkBridge& _args) : link(_link), id(_id), args(_args), mgmtObject(0), - listener(l), name(Uuid(true).str()), persistenceId(0) + listener(l), name(Uuid(true).str()), queueName("bridge_queue_"), persistenceId(0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + std::stringstream title; + title << id << "_" << link->getBroker()->getFederationTag(); + queueName += title.str(); + ManagementAgent* agent = link->getBroker()->getManagementAgent(); if (agent != 0) { - mgmtObject = new management::Bridge(agent, this, link, id, args.i_durable, args.i_src, args.i_dest, - args.i_key, args.i_srcIsQueue, args.i_srcIsLocal, - args.i_tag, args.i_excludes); + mgmtObject = new _qmf::Bridge + (agent, this, link, id, args.i_durable, args.i_src, args.i_dest, + args.i_key, args.i_srcIsQueue, args.i_srcIsLocal, + args.i_tag, args.i_excludes, args.i_dynamic, args.i_sync); if (!args.i_durable) agent->addObject(mgmtObject); } + QPID_LOG(debug, "Bridge created from " << args.i_src << " to " << args.i_dest); } Bridge::~Bridge() @@ -55,62 +81,111 @@ Bridge::~Bridge() mgmtObject->resourceDestroy(); } -void Bridge::create(ConnectionState& c) +void Bridge::create(Connection& c) { - channelHandler.reset(new framing::ChannelHandler(id, &(c.getOutput()))); - session.reset(new framing::AMQP_ServerProxy::Session(*channelHandler)); - peer.reset(new framing::AMQP_ServerProxy(*channelHandler)); + connState = &c; + conn = &c; + FieldTable options; + if (args.i_sync) options.setInt("qpid.sync_frequency", args.i_sync); + SessionHandler& sessionHandler = c.getChannel(id); + if (args.i_srcIsLocal) { + if (args.i_dynamic) + throw Exception("Dynamic routing not supported for push routes"); + // Point the bridging commands at the local connection handler + pushHandler.reset(new PushHandler(&c)); + channelHandler.reset(new framing::ChannelHandler(id, pushHandler.get())); - session->attach(name, false); - session->commandPoint(0,0); + session.reset(new framing::AMQP_ServerProxy::Session(*channelHandler)); + peer.reset(new framing::AMQP_ServerProxy(*channelHandler)); + + session->attach(name, false); + session->commandPoint(0,0); + } else { + sessionHandler.attachAs(name); + // Point the bridging commands at the remote peer broker + peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out)); + } - if (args.i_srcIsLocal) { - //TODO: handle 'push' here... simplest way is to create frames and pass them to Connection::received() + if (args.i_srcIsLocal) sessionHandler.getSession()->disableReceiverTracking(); + if (args.i_srcIsQueue) { + peer->getMessage().subscribe(args.i_src, args.i_dest, args.i_sync ? 0 : 1, 0, false, "", 0, options); + peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); + peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + QPID_LOG(debug, "Activated route from queue " << args.i_src << " to " << args.i_dest); } else { - if (args.i_srcIsQueue) { - peer->getMessage().subscribe(args.i_src, args.i_dest, 1, 0, false, "", 0, FieldTable()); - peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); - peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + FieldTable queueSettings; + + if (args.i_tag.size()) { + queueSettings.setString("qpid.trace.id", args.i_tag); } else { - string queue = "bridge_queue_"; - queue += Uuid(true).str(); - FieldTable queueSettings; - if (args.i_tag.size()) { - queueSettings.setString("qpid.trace.id", args.i_tag); - } - if (args.i_excludes.size()) { - queueSettings.setString("qpid.trace.exclude", args.i_excludes); - } - - bool durable = false;//should this be an arg, or would be use srcIsQueue for durable queues? - bool autoDelete = !durable;//auto delete transient queues? - peer->getQueue().declare(queue, "", false, durable, true, autoDelete, queueSettings); - peer->getExchange().bind(queue, args.i_src, args.i_key, FieldTable()); - peer->getMessage().subscribe(queue, args.i_dest, 1, 0, false, "", 0, FieldTable()); - peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); - peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + const string& peerTag = c.getFederationPeerTag(); + if (peerTag.size()) + queueSettings.setString("qpid.trace.id", peerTag); + } + + if (args.i_excludes.size()) { + queueSettings.setString("qpid.trace.exclude", args.i_excludes); + } else { + const string& localTag = link->getBroker()->getFederationTag(); + if (localTag.size()) + queueSettings.setString("qpid.trace.exclude", localTag); + } + + bool durable = false;//should this be an arg, or would we use srcIsQueue for durable queues? + bool autoDelete = !durable;//auto delete transient queues? + peer->getQueue().declare(queueName, "", false, durable, true, autoDelete, queueSettings); + if (!args.i_dynamic) + peer->getExchange().bind(queueName, args.i_src, args.i_key, FieldTable()); + 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); + + if (args.i_dynamic) { + Exchange::shared_ptr exchange = link->getBroker()->getExchanges().get(args.i_src); + if (exchange.get() == 0) + throw Exception("Exchange not found for dynamic route"); + exchange->registerDynamicBridge(this); + QPID_LOG(debug, "Activated dynamic route for exchange " << args.i_src); + } else { + QPID_LOG(debug, "Activated static route from exchange " << args.i_src << " to " << args.i_dest); } } + if (args.i_srcIsLocal) sessionHandler.getSession()->enableReceiverTracking(); } -void Bridge::cancel() +void Bridge::cancel(Connection& c) { + if (args.i_srcIsLocal) { + //recreate peer to be sure that the session handler reference + //is valid (it could have been deleted due to a detach) + SessionHandler& sessionHandler = c.getChannel(id); + peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out)); + } peer->getMessage().cancel(args.i_dest); peer->getSession().detach(name); } +void Bridge::closed() +{ + if (args.i_dynamic) { + Exchange::shared_ptr exchange = link->getBroker()->getExchanges().get(args.i_src); + if (exchange.get() != 0) + exchange->removeDynamicBridge(this); + } +} + void Bridge::destroy() { listener(this); } -void Bridge::setPersistenceId(uint64_t id) const +void Bridge::setPersistenceId(uint64_t pId) const { if (mgmtObject != 0 && persistenceId == 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); - agent->addObject (mgmtObject, id); + ManagementAgent* agent = link->getBroker()->getManagementAgent(); + agent->addObject (mgmtObject, pId); } - persistenceId = id; + persistenceId = pId; } const string& Bridge::getName() const @@ -138,9 +213,11 @@ Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer) bool is_local(buffer.getOctet()); buffer.getShortString(id); buffer.getShortString(excludes); + bool dynamic(buffer.getOctet()); + uint16_t sync = buffer.getShort(); return links.declare(host, port, durable, src, dest, key, - is_queue, is_local, id, excludes).first; + is_queue, is_local, id, excludes, dynamic, sync).first; } void Bridge::encode(Buffer& buffer) const @@ -156,6 +233,8 @@ void Bridge::encode(Buffer& buffer) const buffer.putOctet(args.i_srcIsLocal ? 1 : 0); buffer.putShortString(args.i_tag); buffer.putShortString(args.i_excludes); + buffer.putOctet(args.i_dynamic ? 1 : 0); + buffer.putShort(args.i_sync); } uint32_t Bridge::encodedSize() const @@ -170,7 +249,9 @@ uint32_t Bridge::encodedSize() const + 1 // srcIsQueue + 1 // srcIsLocal + args.i_tag.size() + 1 - + args.i_excludes.size() + 1; + + args.i_excludes.size() + 1 + + 1 // dynamic + + 2; // sync } management::ManagementObject* Bridge::GetManagementObject (void) const @@ -178,9 +259,11 @@ management::ManagementObject* Bridge::GetManagementObject (void) const return (management::ManagementObject*) mgmtObject; } -management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, management::Args& /*args*/) +management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, + management::Args& /*args*/, + string&) { - if (methodId == management::Bridge::METHOD_CLOSE) { + if (methodId == _qmf::Bridge::METHOD_CLOSE) { //notify that we are closed destroy(); return management::Manageable::STATUS_OK; @@ -189,4 +272,53 @@ management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, man } } +void Bridge::propagateBinding(const string& key, const string& tagList, + const string& op, const string& origin) +{ + const string& localTag = link->getBroker()->getFederationTag(); + const string& peerTag = connState->getFederationPeerTag(); + + if (tagList.find(peerTag) == tagList.npos) { + FieldTable bindArgs; + string newTagList(tagList + string(tagList.empty() ? "" : ",") + localTag); + + bindArgs.setString(qpidFedOp, op); + bindArgs.setString(qpidFedTags, newTagList); + if (origin.empty()) + bindArgs.setString(qpidFedOrigin, localTag); + else + bindArgs.setString(qpidFedOrigin, origin); + + conn->requestIOProcessing(boost::bind(&Bridge::ioThreadPropagateBinding, this, + queueName, args.i_src, key, bindArgs)); + } +} + +void Bridge::sendReorigin() +{ + FieldTable bindArgs; + + bindArgs.setString(qpidFedOp, fedOpReorigin); + bindArgs.setString(qpidFedTags, link->getBroker()->getFederationTag()); + + conn->requestIOProcessing(boost::bind(&Bridge::ioThreadPropagateBinding, this, + queueName, args.i_src, args.i_key, bindArgs)); +} + +void Bridge::ioThreadPropagateBinding(const string& queue, const string& exchange, const string& key, FieldTable args) +{ + peer->getExchange().bind(queue, exchange, key, args); +} + +bool Bridge::containsLocalTag(const string& tagList) const +{ + const string& localTag = link->getBroker()->getFederationTag(); + return (tagList.find(localTag) != tagList.npos); +} + +const string& Bridge::getLocalTag() const +{ + return link->getBroker()->getFederationTag(); +} + }} diff --git a/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h index 06fba25268..7dae5c37a1 100644 --- a/cpp/src/qpid/broker/Bridge.h +++ b/cpp/src/qpid/broker/Bridge.h @@ -21,13 +21,16 @@ #ifndef _Bridge_ #define _Bridge_ -#include "PersistableConfig.h" +#include "qpid/broker/PersistableConfig.h" #include "qpid/framing/AMQP_ServerProxy.h" #include "qpid/framing/ChannelHandler.h" #include "qpid/framing/Buffer.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/FieldTable.h" #include "qpid/management/Manageable.h" -#include "qpid/management/ArgsLinkBridge.h" -#include "qpid/management/Bridge.h" +#include "qpid/broker/Exchange.h" +#include "qmf/org/apache/qpid/broker/ArgsLinkBridge.h" +#include "qmf/org/apache/qpid/broker/Bridge.h" #include <boost/function.hpp> #include <memory> @@ -35,26 +38,31 @@ namespace qpid { namespace broker { +class Connection; class ConnectionState; class Link; class LinkRegistry; -class Bridge : public PersistableConfig, public management::Manageable +class Bridge : public PersistableConfig, public management::Manageable, public Exchange::DynamicBridge { public: typedef boost::shared_ptr<Bridge> shared_ptr; typedef boost::function<void(Bridge*)> CancellationListener; - Bridge(Link* link, framing::ChannelId id, CancellationListener l, const management::ArgsLinkBridge& args); + Bridge(Link* link, framing::ChannelId id, CancellationListener l, + const qmf::org::apache::qpid::broker::ArgsLinkBridge& args); ~Bridge(); - void create(ConnectionState& c); - void cancel(); + void create(Connection& c); + void cancel(Connection& c); + void closed(); void destroy(); bool isDurable() { return args.i_durable; } management::ManagementObject* GetManagementObject() const; - management::Manageable::status_t ManagementMethod(uint32_t methodId, management::Args& args); + management::Manageable::status_t ManagementMethod(uint32_t methodId, + management::Args& args, + std::string& text); // PersistableConfig: void setPersistenceId(uint64_t id) const; @@ -64,18 +72,35 @@ public: const std::string& getName() const; static Bridge::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer); + // Exchange::DynamicBridge methods + void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin); + void sendReorigin(); + void ioThreadPropagateBinding(const string& queue, const string& exchange, const string& key, framing::FieldTable args); + bool containsLocalTag(const std::string& tagList) const; + const std::string& getLocalTag() const; + private: + struct PushHandler : framing::FrameHandler { + PushHandler(Connection* c) { conn = c; } + void handle(framing::AMQFrame& frame); + Connection* conn; + }; + + std::auto_ptr<PushHandler> pushHandler; std::auto_ptr<framing::ChannelHandler> channelHandler; std::auto_ptr<framing::AMQP_ServerProxy::Session> session; std::auto_ptr<framing::AMQP_ServerProxy> peer; Link* link; framing::ChannelId id; - management::ArgsLinkBridge args; - management::Bridge* mgmtObject; + qmf::org::apache::qpid::broker::ArgsLinkBridge args; + qmf::org::apache::qpid::broker::Bridge* mgmtObject; CancellationListener listener; std::string name; + std::string queueName; mutable uint64_t persistenceId; + ConnectionState* connState; + Connection* conn; }; diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp index 4d7c07649b..849bf6d1f5 100644 --- a/cpp/src/qpid/broker/Broker.cpp +++ b/cpp/src/qpid/broker/Broker.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 @@ -19,23 +19,27 @@ * */ -#include "config.h" -#include "Broker.h" -#include "DirectExchange.h" -#include "FanOutExchange.h" -#include "HeadersExchange.h" -#include "MessageStoreModule.h" -#include "NullMessageStore.h" -#include "RecoveryManagerImpl.h" -#include "TopicExchange.h" -#include "Link.h" - -#include "qpid/management/PackageQpid.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/broker/MessageStoreModule.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/RecoveryManagerImpl.h" +#include "qpid/broker/SaslAuthenticator.h" +#include "qpid/broker/SecureConnectionFactory.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/ExpiryPolicy.h" + +#include "qmf/org/apache/qpid/broker/Package.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerEcho.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h" #include "qpid/management/ManagementExchange.h" -#include "qpid/management/ArgsBrokerEcho.h" #include "qpid/log/Statement.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/Uuid.h" #include "qpid/sys/ProtocolFactory.h" #include "qpid/sys/Poller.h" #include "qpid/sys/Dispatcher.h" @@ -45,20 +49,14 @@ #include "qpid/sys/ConnectionInputHandlerFactory.h" #include "qpid/sys/TimeoutHandler.h" #include "qpid/sys/SystemInfo.h" +#include "qpid/Address.h" #include "qpid/Url.h" +#include "qpid/Version.h" #include <boost/bind.hpp> #include <iostream> #include <memory> -#include <stdlib.h> - -#if HAVE_SASL -#include <sasl/sasl.h> -static const bool AUTH_DEFAULT=true; -#else -static const bool AUTH_DEFAULT=false; -#endif using qpid::sys::ProtocolFactory; using qpid::sys::Poller; @@ -66,11 +64,11 @@ using qpid::sys::Dispatcher; using qpid::sys::Thread; using qpid::framing::FrameHandler; using qpid::framing::ChannelId; -using qpid::management::ManagementBroker; +using qpid::management::ManagementAgent; using qpid::management::ManagementObject; using qpid::management::Manageable; using qpid::management::Args; -using qpid::management::ArgsBrokerEcho; +namespace _qmf = qmf::org::apache::qpid::broker; namespace qpid { namespace broker { @@ -85,22 +83,26 @@ Broker::Options::Options(const std::string& name) : stagingThreshold(5000000), enableMgmt(1), mgmtPubInterval(10), - auth(AUTH_DEFAULT), + queueCleanInterval(60*10),//10 minutes + auth(SaslAuthenticator::available()), realm("QPID"), replayFlushLimit(0), replayHardLimit(0), queueLimit(100*1048576/*100M default limit*/), - tcpNoDelay(false) + tcpNoDelay(false), + requireEncrypted(false), + maxSessionRate(0), + asyncQueueEvents(true) { int c = sys::SystemInfo::concurrency(); workerThreads=c+1; - char *home = ::getenv("HOME"); + std::string home = getHome(); - if (home == 0) - dataDir += "/tmp"; + if (home.length() == 0) + dataDir += DEFAULT_DATA_DIR_LOCATION; else dataDir += home; - dataDir += "/.qpidd"; + dataDir += DEFAULT_DATA_DIR_NAME; addOptions() ("data-dir", optValue(dataDir,"DIR"), "Directory to contain persistent data generated by the broker") @@ -112,10 +114,16 @@ Broker::Options::Options(const std::string& name) : ("staging-threshold", optValue(stagingThreshold, "N"), "Stages messages over N bytes to disk") ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management") ("mgmt-pub-interval", optValue(mgmtPubInterval, "SECONDS"), "Management Publish Interval") + ("queue-purge-interval", optValue(queueCleanInterval, "SECONDS"), + "Interval between attempts to purge any expired messages from queues") ("auth", optValue(auth, "yes|no"), "Enable authentication, if disabled all incoming connections will be trusted") ("realm", optValue(realm, "REALM"), "Use the given realm when performing authentication") ("default-queue-limit", optValue(queueLimit, "BYTES"), "Default maximum size for queues (in bytes)") - ("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections"); + ("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections") + ("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted") + ("known-hosts-url", optValue(knownHosts, "URL or 'none'"), "URL to send as 'known-hosts' to clients ('none' implies empty list)") + ("max-session-rate", optValue(maxSessionRate, "MESSAGES/S"), "Sets the maximum message rate per session (0=unlimited)") + ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication"); } const std::string empty; @@ -124,79 +132,99 @@ const std::string amq_topic("amq.topic"); const std::string amq_fanout("amq.fanout"); const std::string amq_match("amq.match"); const std::string qpid_management("qpid.management"); +const std::string knownHostsNone("none"); Broker::Broker(const Broker::Options& conf) : poller(new Poller), config(conf), - managementAgentSingleton(!config.enableMgmt), - store(0), - acl(0), - dataDir(conf.noDataDir ? std::string () : conf.dataDir), + managementAgent(conf.enableMgmt ? new ManagementAgent() : 0), + store(new NullMessageStore), + acl(0), + dataDir(conf.noDataDir ? std::string() : conf.dataDir), + queues(this), + exchanges(this), links(this), - factory(*this), + factory(new SecureConnectionFactory(*this)), + dtxManager(timer), sessionManager( qpid::SessionState::Configuration( conf.replayFlushLimit*1024, // convert kb to bytes. conf.replayHardLimit*1024), - *this) + *this), + queueCleaner(queues, timer), + queueEvents(poller,!conf.asyncQueueEvents), + recovery(true), + clusterUpdatee(false), + expiryPolicy(new ExpiryPolicy), + connectionCounter(conf.maxConnections), + getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)) { - if(conf.enableMgmt){ + if (conf.enableMgmt) { QPID_LOG(info, "Management enabled"); - managementAgent = managementAgentSingleton.getInstance(); - ((ManagementBroker*) managementAgent)->configure - (dataDir.isEnabled () ? dataDir.getPath () : string (), - conf.mgmtPubInterval, this, conf.workerThreads + 3); - qpid::management::PackageQpid packageInitializer (managementAgent); - - System* system = new System (dataDir.isEnabled () ? dataDir.getPath () : string ()); - systemObject = System::shared_ptr (system); - - mgmtObject = new management::Broker (managementAgent, this, system, conf.port); - mgmtObject->set_workerThreads (conf.workerThreads); - mgmtObject->set_maxConns (conf.maxConnections); - mgmtObject->set_connBacklog (conf.connectionBacklog); - mgmtObject->set_stagingThreshold (conf.stagingThreshold); - mgmtObject->set_mgmtPubInterval (conf.mgmtPubInterval); - mgmtObject->set_version (PACKAGE_VERSION); - mgmtObject->set_dataDirEnabled (dataDir.isEnabled ()); - mgmtObject->set_dataDir (dataDir.getPath ()); - - managementAgent->addObject (mgmtObject, 2, 1); + managementAgent->configure(dataDir.isEnabled() ? dataDir.getPath() : string(), + conf.mgmtPubInterval, this, conf.workerThreads + 3); + _qmf::Package packageInitializer(managementAgent.get()); + + System* system = new System (dataDir.isEnabled() ? dataDir.getPath() : string(), this); + systemObject = System::shared_ptr(system); + + mgmtObject = new _qmf::Broker(managementAgent.get(), this, system, conf.port); + mgmtObject->set_workerThreads(conf.workerThreads); + mgmtObject->set_maxConns(conf.maxConnections); + mgmtObject->set_connBacklog(conf.connectionBacklog); + mgmtObject->set_stagingThreshold(conf.stagingThreshold); + mgmtObject->set_mgmtPubInterval(conf.mgmtPubInterval); + mgmtObject->set_version(qpid::version); + if (dataDir.isEnabled()) + mgmtObject->set_dataDir(dataDir.getPath()); + else + mgmtObject->clr_dataDir(); + + managementAgent->addObject(mgmtObject, 0x1000000000000002LL); // Since there is currently no support for virtual hosts, a placeholder object // representing the implied single virtual host is added here to keep the // management schema correct. - Vhost* vhost = new Vhost (this); - vhostObject = Vhost::shared_ptr (vhost); - - queues.setParent (vhost); - exchanges.setParent (vhost); - links.setParent (vhost); + Vhost* vhost = new Vhost(this, this); + vhostObject = Vhost::shared_ptr(vhost); + framing::Uuid uuid(managementAgent->getUuid()); + federationTag = uuid.str(); + vhostObject->setFederationTag(federationTag); + + queues.setParent(vhost); + exchanges.setParent(vhost); + links.setParent(vhost); + } else { + // Management is disabled so there is no broker management ID. + // Create a unique uuid to use as the federation tag. + framing::Uuid uuid(true); + federationTag = uuid.str(); } QueuePolicy::setDefaultMaxSize(conf.queueLimit); + queues.setQueueEvents(&queueEvents); // Early-Initialize plugins - const Plugin::Plugins& plugins=Plugin::getPlugins(); - for (Plugin::Plugins::const_iterator i = plugins.begin(); - i != plugins.end(); - i++) - (*i)->earlyInitialize(*this); + Plugin::earlyInitAll(*this); // If no plugin store module registered itself, set up the null store. - if (store == 0) - setStore (new NullMessageStore (false)); - - queues.setStore (store); - dtxManager.setStore (store); - links.setStore (store); + if (NullMessageStore::isNullStore(store.get())) + setStore(); exchanges.declare(empty, DirectExchange::typeName); // Default exchange. - - if (store != 0) { - RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager, - conf.stagingThreshold); - store->recover(recoverer); + + if (store.get() != 0) { + // The cluster plug-in will setRecovery(false) on all but the first + // broker to join a cluster. + if (getRecovery()) { + RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager, + conf.stagingThreshold); + store->recover(recoverer); + } + else { + QPID_LOG(notice, "Cluster recovery: recovered journal data discarded and journal files pushed down"); + store->truncateInit(true); // save old files in subdir + } } //ensure standard exchanges exist (done after recovery from store) @@ -209,9 +237,8 @@ Broker::Broker(const Broker::Options& conf) : exchanges.declare(qpid_management, ManagementExchange::typeName); Exchange::shared_ptr mExchange = exchanges.get (qpid_management); Exchange::shared_ptr dExchange = exchanges.get (amq_direct); - ((ManagementBroker*) managementAgent)->setExchange (mExchange, dExchange); - dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent - ((ManagementBroker*) managementAgent); + managementAgent->setExchange(mExchange, dExchange); + boost::dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent(managementAgent.get()); } else QPID_LOG(info, "Management not enabled"); @@ -220,29 +247,33 @@ Broker::Broker(const Broker::Options& conf) : * SASL setup, can fail and terminate startup */ if (conf.auth) { -#if HAVE_SASL - int code = sasl_server_init(NULL, BROKER_SASL_NAME); - if (code != SASL_OK) { - // TODO: Figure out who owns the char* returned by - // sasl_errstring, though it probably does not matter much - throw Exception(sasl_errstring(code, NULL, NULL)); - } + SaslAuthenticator::init(qpid::saslName); QPID_LOG(info, "SASL enabled"); -#else - throw Exception("Requested authentication but SASL unavailable"); -#endif + } else { + QPID_LOG(notice, "SASL disabled: No Authentication Performed"); } // Initialize plugins - for (Plugin::Plugins::const_iterator i = plugins.begin(); - i != plugins.end(); - i++) - (*i)->initialize(*this); + Plugin::initializeAll(*this); + + if (conf.queueCleanInterval) { + queueCleaner.start(conf.queueCleanInterval * qpid::sys::TIME_SEC); + } + + //initialize known broker urls (TODO: add support for urls for other transports (SSL, RDMA)): + if (conf.knownHosts.empty()) { + boost::shared_ptr<ProtocolFactory> factory = getProtocolFactory(TCP_TRANSPORT); + if (factory) { + knownBrokers.push_back ( qpid::Url::getIpAddressesUrl ( factory->getPort() ) ); + } + } else if (conf.knownHosts != knownHostsNone) { + knownBrokers.push_back(Url(conf.knownHosts)); + } } void Broker::declareStandardExchange(const std::string& name, const std::string& type) { - bool storeEnabled = store != NULL; + bool storeEnabled = store.get() != NULL; std::pair<Exchange::shared_ptr, bool> status = exchanges.declare(name, type, storeEnabled); if (status.second && storeEnabled) { store->create(*status.first, framing::FieldTable ()); @@ -250,28 +281,32 @@ void Broker::declareStandardExchange(const std::string& name, const std::string& } -boost::intrusive_ptr<Broker> Broker::create(int16_t port) +boost::intrusive_ptr<Broker> Broker::create(int16_t port) { Options config; config.port=port; return create(config); } -boost::intrusive_ptr<Broker> Broker::create(const Options& opts) +boost::intrusive_ptr<Broker> Broker::create(const Options& opts) { return boost::intrusive_ptr<Broker>(new Broker(opts)); } -void Broker::setStore (MessageStore* _store) +void Broker::setStore (boost::shared_ptr<MessageStore>& _store) { - assert (store == 0 && _store != 0); - if (store == 0 && _store != 0) - store = new MessageStoreModule (_store); + store.reset(new MessageStoreModule (_store)); + setStore(); +} + +void Broker::setStore () { + queues.setStore (store.get()); + dtxManager.setStore (store.get()); + links.setStore (store.get()); } void Broker::run() { - accept(); - + QPID_LOG(notice, "Broker running"); Dispatcher d(poller); int numIOThreads = config.workerThreads; std::vector<Thread> t(numIOThreads-1); @@ -282,7 +317,7 @@ void Broker::run() { // Run final thread d.run(); - + // Now wait for n-1 io threads to exit for (int i=0; i<numIOThreads-1; ++i) { t[i].join(); @@ -298,13 +333,10 @@ void Broker::shutdown() { Broker::~Broker() { shutdown(); + queueEvents.shutdown(); finalize(); // Finalize any plugins. - delete store; - if (config.auth) { -#if HAVE_SASL - sasl_done(); -#endif - } + if (config.auth) + SaslAuthenticator::fini(); QPID_LOG(notice, "Shut down"); } @@ -319,7 +351,8 @@ Manageable* Broker::GetVhostObject(void) const } Manageable::status_t Broker::ManagementMethod (uint32_t methodId, - Args& args) + Args& args, + string&) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; @@ -327,27 +360,36 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId, switch (methodId) { - case management::Broker::METHOD_ECHO : + case _qmf::Broker::METHOD_ECHO : status = Manageable::STATUS_OK; break; - case management::Broker::METHOD_CONNECT : { - management::ArgsBrokerConnect& hp= - dynamic_cast<management::ArgsBrokerConnect&>(args); - - if (hp.i_useSsl) - return Manageable::STATUS_FEATURE_NOT_IMPLEMENTED; - + case _qmf::Broker::METHOD_CONNECT : { + _qmf::ArgsBrokerConnect& hp= + dynamic_cast<_qmf::ArgsBrokerConnect&>(args); + + string transport = hp.i_transport.empty() ? TCP_TRANSPORT : hp.i_transport; + if (!getProtocolFactory(transport)) { + QPID_LOG(error, "Transport '" << transport << "' not supported"); + return Manageable::STATUS_NOT_IMPLEMENTED; + } std::pair<Link::shared_ptr, bool> response = - links.declare (hp.i_host, hp.i_port, hp.i_useSsl, hp.i_durable, + links.declare (hp.i_host, hp.i_port, transport, hp.i_durable, hp.i_authMechanism, hp.i_username, hp.i_password); if (hp.i_durable && response.second) store->create(*response.first); - status = Manageable::STATUS_OK; break; } - case management::Broker::METHOD_JOINCLUSTER : - case management::Broker::METHOD_LEAVECLUSTER : + case _qmf::Broker::METHOD_QUEUEMOVEMESSAGES : { + _qmf::ArgsBrokerQueueMoveMessages& moveArgs= + dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args); + if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty)) + status = Manageable::STATUS_OK; + else + return Manageable::STATUS_PARAMETER_INVALID; + break; + } + default: status = Manageable::STATUS_NOT_IMPLEMENTED; break; } @@ -355,34 +397,40 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId, return status; } -boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory() const { - assert(protocolFactories.size() > 0); - return protocolFactories[0]; +boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory(const std::string& name) const { + ProtocolFactoryMap::const_iterator i + = name.empty() ? protocolFactories.begin() : protocolFactories.find(name); + if (i == protocolFactories.end()) return boost::shared_ptr<ProtocolFactory>(); + else return i->second; } -void Broker::registerProtocolFactory(ProtocolFactory::shared_ptr protocolFactory) { - protocolFactories.push_back(protocolFactory); +uint16_t Broker::getPort(const std::string& name) const { + boost::shared_ptr<ProtocolFactory> factory = getProtocolFactory(name); + if (factory) { + return factory->getPort(); + } else { + throw NoSuchTransportException(QPID_MSG("No such transport: '" << name << "'")); + } } -// TODO: This can only work if there is only one protocolFactory -uint16_t Broker::getPort() const { - return getProtocolFactory()->getPort(); +void Broker::registerProtocolFactory(const std::string& name, ProtocolFactory::shared_ptr protocolFactory) { + protocolFactories[name] = protocolFactory; } -// TODO: This should iterate over all protocolFactories void Broker::accept() { - for (unsigned int i = 0; i < protocolFactories.size(); ++i) - protocolFactories[i]->accept(poller, &factory); + for (ProtocolFactoryMap::const_iterator i = protocolFactories.begin(); i != protocolFactories.end(); i++) { + i->second->accept(poller, factory.get()); + } } - -// TODO: How to chose the protocolFactory to use for the connection void Broker::connect( - const std::string& host, uint16_t port, bool /*useSsl*/, + const std::string& host, uint16_t port, const std::string& transport, boost::function2<void, int, std::string> failed, sys::ConnectionCodec::Factory* f) { - getProtocolFactory()->connect(poller, host, port, f ? f : &factory, failed); + boost::shared_ptr<ProtocolFactory> pf = getProtocolFactory(transport); + if (pf) pf->connect(poller, host, port, f ? f : factory.get(), failed); + else throw NoSuchTransportException(QPID_MSG("Unsupported transport type: " << transport)); } void Broker::connect( @@ -391,11 +439,35 @@ void Broker::connect( sys::ConnectionCodec::Factory* f) { url.throwIfEmpty(); - TcpAddress addr=boost::get<TcpAddress>(url[0]); - connect(addr.host, addr.port, false, failed, f); + const TcpAddress* addr=url[0].get<TcpAddress>(); + connect(addr->host, addr->port, TCP_TRANSPORT, failed, f); } +uint32_t Broker::queueMoveMessages( + const std::string& srcQueue, + const std::string& destQueue, + uint32_t qty) +{ + Queue::shared_ptr src_queue = queues.find(srcQueue); + if (!src_queue) + return 0; + Queue::shared_ptr dest_queue = queues.find(destQueue); + if (!dest_queue) + return 0; + + return src_queue->move(dest_queue, qty); +} + + boost::shared_ptr<sys::Poller> Broker::getPoller() { return poller; } +std::vector<Url> +Broker::getKnownBrokersImpl() +{ + return knownBrokers; +} + +const std::string Broker::TCP_TRANSPORT("tcp"); + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h index f7399c375f..b85aa7d96c 100644 --- a/cpp/src/qpid/broker/Broker.h +++ b/cpp/src/qpid/broker/Broker.h @@ -22,21 +22,25 @@ * */ -#include "ConnectionFactory.h" -#include "ConnectionToken.h" -#include "DirectExchange.h" -#include "DtxManager.h" -#include "ExchangeRegistry.h" -#include "MessageStore.h" -#include "QueueRegistry.h" -#include "LinkRegistry.h" -#include "SessionManager.h" -#include "Vhost.h" -#include "System.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/ConnectionFactory.h" +#include "qpid/broker/ConnectionToken.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/DtxManager.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid/broker/LinkRegistry.h" +#include "qpid/broker/SessionManager.h" +#include "qpid/broker/QueueCleaner.h" +#include "qpid/broker/QueueEvents.h" +#include "qpid/broker/Vhost.h" +#include "qpid/broker/System.h" +#include "qpid/broker/ExpiryPolicy.h" #include "qpid/management/Manageable.h" -#include "qpid/management/ManagementBroker.h" -#include "qpid/management/Broker.h" -#include "qpid/management/ArgsBrokerConnect.h" +#include "qpid/management/ManagementAgent.h" +#include "qmf/org/apache/qpid/broker/Broker.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerConnect.h" #include "qpid/Options.h" #include "qpid/Plugin.h" #include "qpid/DataDir.h" @@ -44,10 +48,13 @@ #include "qpid/framing/OutputHandler.h" #include "qpid/framing/ProtocolInitiation.h" #include "qpid/sys/Runnable.h" +#include "qpid/sys/Timer.h" #include "qpid/RefCounted.h" -#include "AclModule.h" +#include "qpid/broker/AclModule.h" +#include "qpid/sys/Mutex.h" #include <boost/intrusive_ptr.hpp> +#include <string> #include <vector> namespace qpid { @@ -57,22 +64,34 @@ namespace sys { class Poller; } -class Url; +struct Url; namespace broker { +class ExpiryPolicy; + static const uint16_t DEFAULT_PORT=5672; +struct NoSuchTransportException : qpid::Exception +{ + NoSuchTransportException(const std::string& s) : Exception(s) {} + virtual ~NoSuchTransportException() throw() {} +}; + /** * A broker instance. */ class Broker : public sys::Runnable, public Plugin::Target, - public management::Manageable, public RefCounted + public management::Manageable, + public RefCounted { - public: +public: struct Options : public qpid::Options { - Options(const std::string& name="Broker Options"); + static const std::string DEFAULT_DATA_DIR_LOCATION; + static const std::string DEFAULT_DATA_DIR_NAME; + + QPID_BROKER_EXTERN Options(const std::string& name="Broker Options"); bool noDataDir; std::string dataDir; @@ -83,45 +102,82 @@ class Broker : public sys::Runnable, public Plugin::Target, uint64_t stagingThreshold; bool enableMgmt; uint16_t mgmtPubInterval; + uint16_t queueCleanInterval; bool auth; std::string realm; size_t replayFlushLimit; size_t replayHardLimit; uint queueLimit; bool tcpNoDelay; + bool requireEncrypted; + std::string knownHosts; + uint32_t maxSessionRate; + bool asyncQueueEvents; + + private: + std::string getHome(); + }; + + class ConnectionCounter { + int maxConnections; + int connectionCount; + sys::Mutex connectionCountLock; + public: + ConnectionCounter(int mc): maxConnections(mc),connectionCount(0) {}; + void inc_connectionCount() { + sys::ScopedLock<sys::Mutex> l(connectionCountLock); + connectionCount++; + } + void dec_connectionCount() { + sys::ScopedLock<sys::Mutex> l(connectionCountLock); + connectionCount--; + } + bool allowConnection() { + sys::ScopedLock<sys::Mutex> l(connectionCountLock); + return (maxConnections <= connectionCount); + } }; - + private: + typedef std::map<std::string, boost::shared_ptr<sys::ProtocolFactory> > ProtocolFactoryMap; + + void declareStandardExchange(const std::string& name, const std::string& type); + void setStore (); + boost::shared_ptr<sys::Poller> poller; + sys::Timer timer; Options config; - management::ManagementAgent::Singleton managementAgentSingleton; - std::vector< boost::shared_ptr<sys::ProtocolFactory> > protocolFactories; - MessageStore* store; - AclModule* acl; + std::auto_ptr<management::ManagementAgent> managementAgent; + ProtocolFactoryMap protocolFactories; + std::auto_ptr<MessageStore> store; + AclModule* acl; DataDir dataDir; QueueRegistry queues; ExchangeRegistry exchanges; LinkRegistry links; - ConnectionFactory factory; + boost::shared_ptr<sys::ConnectionCodec::Factory> factory; DtxManager dtxManager; SessionManager sessionManager; - management::ManagementAgent* managementAgent; - management::Broker* mgmtObject; + qmf::org::apache::qpid::broker::Broker* mgmtObject; Vhost::shared_ptr vhostObject; System::shared_ptr systemObject; - - void declareStandardExchange(const std::string& name, const std::string& type); - - + QueueCleaner queueCleaner; + QueueEvents queueEvents; + std::vector<Url> knownBrokers; + std::vector<Url> getKnownBrokersImpl(); + std::string federationTag; + bool recovery; + bool clusterUpdatee; + boost::intrusive_ptr<ExpiryPolicy> expiryPolicy; + ConnectionCounter connectionCounter; + public: - - virtual ~Broker(); - Broker(const Options& configuration); - static boost::intrusive_ptr<Broker> create(const Options& configuration); - static boost::intrusive_ptr<Broker> create(int16_t port = DEFAULT_PORT); + QPID_BROKER_EXTERN Broker(const Options& configuration); + static QPID_BROKER_EXTERN boost::intrusive_ptr<Broker> create(const Options& configuration); + static QPID_BROKER_EXTERN boost::intrusive_ptr<Broker> create(int16_t port = DEFAULT_PORT); /** * Return listening port. If called before bind this is @@ -129,7 +185,7 @@ class Broker : public sys::Runnable, public Plugin::Target, * port, which will be different if the configured port is * 0. */ - virtual uint16_t getPort() const; + virtual uint16_t getPort(const std::string& name) const; /** * Run the broker. Implements Runnable::run() so the broker @@ -140,7 +196,7 @@ class Broker : public sys::Runnable, public Plugin::Target, /** Shut down the broker */ virtual void shutdown(); - void setStore (MessageStore*); + QPID_BROKER_EXTERN void setStore (boost::shared_ptr<MessageStore>& store); MessageStore& getStore() { return *store; } void setAcl (AclModule* _acl) {acl = _acl;} AclModule* getAcl() { return acl; } @@ -151,21 +207,29 @@ class Broker : public sys::Runnable, public Plugin::Target, DtxManager& getDtxManager() { return dtxManager; } DataDir& getDataDir() { return dataDir; } Options& getOptions() { return config; } + QueueEvents& getQueueEvents() { return queueEvents; } + + void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { expiryPolicy = e; } + boost::intrusive_ptr<ExpiryPolicy> getExpiryPolicy() { return expiryPolicy; } 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); - + management::Manageable::status_t ManagementMethod (uint32_t methodId, + management::Args& args, + std::string& text); + /** Add to the broker's protocolFactorys */ - void registerProtocolFactory(boost::shared_ptr<sys::ProtocolFactory>); + void registerProtocolFactory(const std::string& name, boost::shared_ptr<sys::ProtocolFactory>); /** Accept connections */ - void accept(); + QPID_BROKER_EXTERN void accept(); /** Create a connection to another broker. */ - void connect(const std::string& host, uint16_t port, bool useSsl, + void connect(const std::string& host, uint16_t port, + const std::string& transport, boost::function2<void, int, std::string> failed, sys::ConnectionCodec::Factory* =0); /** Create a connection to another broker. */ @@ -173,16 +237,38 @@ class Broker : public sys::Runnable, public Plugin::Target, boost::function2<void, int, std::string> failed, sys::ConnectionCodec::Factory* =0); - // TODO: There isn't a single ProtocolFactory so the use of the following needs to be fixed - // For the present just return the first ProtocolFactory registered. - boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory() const; + /** 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); + + 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(); + 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; } + + sys::Timer& getTimer() { return timer; } + + boost::function<std::vector<Url> ()> getKnownBrokers; + + static QPID_BROKER_EXTERN const std::string TCP_TRANSPORT; + + void setRecovery(bool set) { recovery = set; } + bool getRecovery() const { return recovery; } + + void setClusterUpdatee(bool set) { clusterUpdatee = set; } + bool isClusterUpdatee() const { return clusterUpdatee; } + + management::ManagementAgent* getManagementAgent() { return managementAgent.get(); } + + ConnectionCounter& getConnectionCounter() {return connectionCounter;} }; }} - - #endif /*!_Broker_*/ diff --git a/cpp/src/qpid/broker/BrokerImportExport.h b/cpp/src/qpid/broker/BrokerImportExport.h new file mode 100644 index 0000000000..4edf8c9844 --- /dev/null +++ b/cpp/src/qpid/broker/BrokerImportExport.h @@ -0,0 +1,33 @@ +#ifndef QPID_BROKER_IMPORT_EXPORT_H +#define QPID_BROKER_IMPORT_EXPORT_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if defined(WIN32) && !defined(QPID_BROKER_STATIC) +#if defined(BROKER_EXPORT) || defined (qpidbroker_EXPORTS) +#define QPID_BROKER_EXTERN __declspec(dllexport) +#else +#define QPID_BROKER_EXTERN __declspec(dllimport) +#endif +#else +#define QPID_BROKER_EXTERN +#endif + +#endif diff --git a/cpp/src/qpid/broker/BrokerSingleton.cpp b/cpp/src/qpid/broker/BrokerSingleton.cpp deleted file mode 100644 index 5ba8c9d1e1..0000000000 --- a/cpp/src/qpid/broker/BrokerSingleton.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "BrokerSingleton.h" - -namespace qpid { -namespace broker { - -BrokerSingleton::BrokerSingleton() { - if (broker.get() == 0) - broker = Broker::create(); - boost::intrusive_ptr<Broker>::operator=(broker); -} - -BrokerSingleton::~BrokerSingleton() { - broker->shutdown(); -} - -boost::intrusive_ptr<Broker> BrokerSingleton::broker; - -}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/BrokerSingleton.h b/cpp/src/qpid/broker/BrokerSingleton.h deleted file mode 100644 index 22b707506b..0000000000 --- a/cpp/src/qpid/broker/BrokerSingleton.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _broker_BrokerSingleton_h -#define _broker_BrokerSingleton_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 "Broker.h" - -namespace qpid { -namespace broker { - -/** - * BrokerSingleton is a smart pointer to a process-wide singleton broker - * started on an os-chosen port. The broker starts the first time - * an instance of BrokerSingleton is created and runs untill the process exits. - * - * Useful for unit tests that want to share a broker between multiple - * tests to reduce overhead of starting/stopping a broker for every test. - * - * Tests that need a new broker can create it directly. - * - * THREAD UNSAFE. - */ -class BrokerSingleton : public boost::intrusive_ptr<Broker> -{ - public: - BrokerSingleton(); - ~BrokerSingleton(); - private: - static boost::intrusive_ptr<Broker> broker; -}; - -}} // namespace qpid::broker - - - -#endif /*!_broker_BrokerSingleton_h*/ diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp index ab18d1f035..17de83e033 100644 --- a/cpp/src/qpid/broker/Connection.cpp +++ b/cpp/src/qpid/broker/Connection.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -18,14 +18,17 @@ * under the License. * */ -#include "Connection.h" -#include "SessionState.h" -#include "Bridge.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/SessionState.h" +#include "qpid/broker/Bridge.h" +#include "qpid/broker/Broker.h" #include "qpid/log/Statement.h" #include "qpid/ptr_map.h" #include "qpid/framing/AMQP_ClientProxy.h" -#include "qpid/agent/ManagementAgent.h" +#include "qpid/framing/enum.h" +#include "qmf/org/apache/qpid/broker/EventClientConnect.h" +#include "qmf/org/apache/qpid/broker/EventClientDisconnect.h" #include <boost/bind.hpp> #include <boost/ptr_container/ptr_vector.hpp> @@ -34,30 +37,53 @@ #include <iostream> #include <assert.h> -using namespace boost; using namespace qpid::sys; using namespace qpid::framing; -using namespace qpid::sys; using qpid::ptr_map_ptr; using qpid::management::ManagementAgent; using qpid::management::ManagementObject; using qpid::management::Manageable; using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; namespace qpid { namespace broker { -Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, bool isLink_) : +struct ConnectionTimeoutTask : public sys::TimerTask { + sys::Timer& timer; + Connection& connection; + + ConnectionTimeoutTask(uint16_t hb, sys::Timer& t, Connection& c) : + TimerTask(Duration(hb*2*TIME_SEC)), + timer(t), + connection(c) + {} + + void touch() { + restart(); + } + + void fire() { + // If we get here then we've not received any traffic in the timeout period + // Schedule closing the connection for the io thread + QPID_LOG(error, "Connection timed out: closing"); + connection.abort(); + } +}; + +Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, unsigned int ssf, bool isLink_, uint64_t objectId) : ConnectionState(out_, broker_), - receivedFn(boost::bind(&Connection::receivedImpl, this, _1)), - closedFn(boost::bind(&Connection::closedImpl, this)), - doOutputFn(boost::bind(&Connection::doOutputImpl, this)), + ssf(ssf), adapter(*this, isLink_), isLink(isLink_), mgmtClosing(false), mgmtId(mgmtId_), mgmtObject(0), - links(broker_.getLinks()) + links(broker_.getLinks()), + agent(0), + timer(broker_.getTimer()), + errorListener(0), + shadow(false) { Manageable* parent = broker.GetVhostObject(); @@ -66,33 +92,48 @@ Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std if (parent != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + agent = broker_.getManagementAgent(); - if (agent != 0) - mgmtObject = new management::Connection(agent, this, parent, mgmtId, !isLink); - agent->addObject(mgmtObject); - } - Plugin::initializeAll(*this); // Let plug-ins update extension points. + // TODO set last bool true if system connection + if (agent != 0) { + mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !isLink, false); + agent->addObject(mgmtObject, objectId, true); + } + ConnectionState::setUrl(mgmtId); + } + if (!isShadow()) broker.getConnectionCounter().inc_connectionCount(); } void Connection::requestIOProcessing(boost::function0<void> callback) { - ioCallback = callback; - out->activateOutput(); + ScopedLock<Mutex> l(ioCallbackLock); + ioCallbacks.push(callback); + out.activateOutput(); } Connection::~Connection() { - if (mgmtObject != 0) + if (mgmtObject != 0) { mgmtObject->resourceDestroy(); + if (!isLink) + agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId())); + } if (isLink) links.notifyClosed(mgmtId); + + if (heartbeatTimer) + heartbeatTimer->cancel(); + if (timeoutTimer) + timeoutTimer->cancel(); + + if (!isShadow()) broker.getConnectionCounter().dec_connectionCount(); } -void Connection::received(framing::AMQFrame& frame) { receivedFn(frame); } +void Connection::received(framing::AMQFrame& frame) { + // Received frame on connection so delay timeout + restartTimeout(); -void Connection::receivedImpl(framing::AMQFrame& frame){ if (frame.getChannel() == 0 && frame.getMethod()) { adapter.handle(frame); } else { @@ -110,7 +151,7 @@ void Connection::recordFromServer(framing::AMQFrame& frame) if (mgmtObject != 0) { mgmtObject->inc_framesToClient(); - mgmtObject->inc_bytesToClient(frame.size()); + mgmtObject->inc_bytesToClient(frame.encodedSize()); } } @@ -119,7 +160,7 @@ void Connection::recordFromClient(framing::AMQFrame& frame) if (mgmtObject != 0) { mgmtObject->inc_framesFromClient(); - mgmtObject->inc_bytesFromClient(frame.size()); + mgmtObject->inc_bytesFromClient(frame.encodedSize()); } } @@ -156,29 +197,55 @@ void Connection::notifyConnectionForced(const string& text) void Connection::setUserId(const string& userId) { ConnectionState::setUserId(userId); - if (mgmtObject != 0) + if (mgmtObject != 0) { mgmtObject->set_authIdentity(userId); + agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId)); + } } -void Connection::close( - ReplyCode code, const string& text, ClassId classId, MethodId methodId) +void Connection::setFederationLink(bool b) { - adapter.close(code, text, classId, methodId); + ConnectionState::setFederationLink(b); + if (mgmtObject != 0) + mgmtObject->set_federationLink(b); +} + +void Connection::close(connection::CloseCode code, const string& text) +{ + QPID_LOG_IF(error, code != connection::CLOSE_CODE_NORMAL, "Connection " << mgmtId << " closed by error: " << text << "(" << code << ")"); + if (heartbeatTimer) + heartbeatTimer->cancel(); + if (timeoutTimer) + timeoutTimer->cancel(); + adapter.close(code, text); + //make sure we delete dangling pointers from outputTasks before deleting sessions + outputTasks.removeAll(); channels.clear(); getOutput().close(); } +// Send a close to the client but keep the channels. Used by cluster. +void Connection::sendClose() { + if (heartbeatTimer) + heartbeatTimer->cancel(); + if (timeoutTimer) + timeoutTimer->cancel(); + adapter.close(connection::CLOSE_CODE_NORMAL, "OK"); + getOutput().close(); +} + void Connection::idleOut(){} void Connection::idleIn(){} -void Connection::closed() { closedFn(); } - -void Connection::closedImpl(){ // Physically closed, suspend open sessions. +void Connection::closed(){ // Physically closed, suspend open sessions. + if (heartbeatTimer) + heartbeatTimer->cancel(); + if (timeoutTimer) + timeoutTimer->cancel(); try { - while (!channels.empty()) + while (!channels.empty()) ptr_map_ptr(channels.begin())->handleDetach(); - // FIXME aconway 2008-07-15: exclusive is per-session not per-connection in 0-10. while (!exclusiveQueues.empty()) { Queue::shared_ptr q(exclusiveQueues.front()); q->releaseExclusiveOwnership(); @@ -195,27 +262,36 @@ void Connection::closedImpl(){ // Physically closed, suspend open sessions. bool Connection::hasOutput() { return outputTasks.hasOutput(); } -bool Connection::doOutput() { return doOutputFn(); } - -bool Connection::doOutputImpl() { - try{ - if (ioCallback) - ioCallback(); // Lend the IO thread for management processing - ioCallback = 0; - - if (mgmtClosing) - close(403, "Closed by Management Request", 0, 0); - else +bool Connection::doOutput() { + try { + { + ScopedLock<Mutex> l(ioCallbackLock); + while (!ioCallbacks.empty()) { + boost::function0<void> cb = ioCallbacks.front(); + ioCallbacks.pop(); + ScopedUnlock<Mutex> ul(ioCallbackLock); + cb(); // Lend the IO thread for management processing + } + } + if (mgmtClosing) { + closed(); + close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request"); + } else { //then do other output as needed: return outputTasks.doOutput(); + } }catch(ConnectionException& e){ - close(e.code, e.getMessage(), 0, 0); + close(e.code, e.getMessage()); }catch(std::exception& e){ - close(541/*internal error*/, e.what(), 0, 0); + close(connection::CLOSE_CODE_CONNECTION_FORCED, e.what()); } return false; } +void Connection::sendHeartbeat() { + adapter.heartbeat(); +} + void Connection::closeChannel(uint16_t id) { ChannelMap::iterator i = channels.find(id); if (i != channels.end()) channels.erase(i); @@ -234,7 +310,7 @@ ManagementObject* Connection::GetManagementObject(void) const return (ManagementObject*) mgmtObject; } -Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&) +Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&, string&) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; @@ -242,10 +318,10 @@ Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&) switch (methodId) { - case management::Connection::METHOD_CLOSE : + case _qmf::Connection::METHOD_CLOSE : mgmtClosing = true; if (mgmtObject != 0) mgmtObject->set_closing(1); - out->activateOutput(); + out.activateOutput(); status = Manageable::STATUS_OK; break; } @@ -253,5 +329,55 @@ Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&) return status; } -}} +void Connection::setSecureConnection(SecureConnection* s) +{ + adapter.setSecureConnection(s); +} + +struct ConnectionHeartbeatTask : public sys::TimerTask { + sys::Timer& timer; + Connection& connection; + ConnectionHeartbeatTask(uint16_t hb, sys::Timer& t, Connection& c) : + TimerTask(Duration(hb*TIME_SEC)), + timer(t), + connection(c) + {} + + void fire() { + // Setup next firing + setupNextFire(); + timer.add(this); + + // Send Heartbeat + connection.sendHeartbeat(); + } +}; +void Connection::abort() +{ + // Make sure that we don't try to send a heartbeat as we're + // aborting the connection + if (heartbeatTimer) + heartbeatTimer->cancel(); + + out.abort(); +} + +void Connection::setHeartbeatInterval(uint16_t heartbeat) +{ + setHeartbeat(heartbeat); + if (heartbeat > 0 && !isShadow()) { + heartbeatTimer = new ConnectionHeartbeatTask(heartbeat, timer, *this); + timer.add(heartbeatTimer); + timeoutTimer = new ConnectionTimeoutTask(heartbeat, timer, *this); + timer.add(timeoutTimer); + } +} + +void Connection::restartTimeout() +{ + if (timeoutTimer) + timeoutTimer->touch(); +} + +}} diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h index 1367f3b9ca..66ede59df5 100644 --- a/cpp/src/qpid/broker/Connection.h +++ b/cpp/src/qpid/broker/Connection.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,52 +25,68 @@ #include <memory> #include <sstream> #include <vector> +#include <queue> #include <boost/ptr_container/ptr_map.hpp> +#include "qpid/broker/ConnectionHandler.h" +#include "qpid/broker/ConnectionState.h" +#include "qpid/broker/SessionHandler.h" +#include "qmf/org/apache/qpid/broker/Connection.h" +#include "qpid/Exception.h" +#include "qpid/RefCounted.h" #include "qpid/framing/AMQFrame.h" -#include "qpid/framing/AMQP_ServerOperations.h" #include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/management/Manageable.h" +#include "qpid/ptr_map.h" #include "qpid/sys/AggregateOutput.h" -#include "qpid/sys/ConnectionOutputHandler.h" #include "qpid/sys/ConnectionInputHandler.h" -#include "qpid/sys/TimeoutHandler.h" -#include "qpid/framing/ProtocolVersion.h" -#include "Broker.h" +#include "qpid/sys/ConnectionOutputHandler.h" #include "qpid/sys/Socket.h" -#include "qpid/Exception.h" -#include "ConnectionHandler.h" -#include "ConnectionState.h" -#include "SessionHandler.h" -#include "qpid/management/Manageable.h" -#include "qpid/management/Connection.h" -#include "qpid/Plugin.h" -#include "qpid/RefCounted.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/sys/Mutex.h" #include <boost/ptr_container/ptr_map.hpp> +#include <boost/bind.hpp> + +#include <algorithm> namespace qpid { namespace broker { +class Broker; class LinkRegistry; +class SecureConnection; +struct ConnectionTimeoutTask; -class Connection : public sys::ConnectionInputHandler, +class Connection : public sys::ConnectionInputHandler, public ConnectionState, - public Plugin::Target, public RefCounted { public: - Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, bool isLink = false); + /** + * Listener that can be registered with a Connection to be informed of errors. + */ + class ErrorListener + { + public: + virtual ~ErrorListener() {} + virtual void sessionError(uint16_t channel, const std::string&) = 0; + virtual void connectionError(const std::string&) = 0; + }; + + Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, unsigned int ssf, + bool isLink = false, uint64_t objectId = 0); ~Connection (); /** Get the SessionHandler for channel. Create if it does not already exist */ SessionHandler& getChannel(framing::ChannelId channel); /** Close the connection */ - void close(framing::ReplyCode code = 403, - const string& text = string(), - framing::ClassId classId = 0, - framing::MethodId methodId = 0); + void close(framing::connection::CloseCode code, const string& text); // ConnectionInputHandler methods void received(framing::AMQFrame& frame); @@ -85,7 +101,7 @@ class Connection : public sys::ConnectionInputHandler, // Manageable entry points management::ManagementObject* GetManagementObject (void) const; management::Manageable::status_t - ManagementMethod (uint32_t methodId, management::Args& args); + ManagementMethod (uint32_t methodId, management::Args& args, std::string&); void requestIOProcessing (boost::function0<void>); void recordFromServer (framing::AMQFrame& frame); @@ -94,29 +110,60 @@ class Connection : public sys::ConnectionInputHandler, std::string getAuthCredentials(); void notifyConnectionForced(const std::string& text); void setUserId(const string& uid); + const std::string& getUserId() const { return ConnectionState::getUserId(); } + const std::string& getMgmtId() const { return mgmtId; } + management::ManagementAgent* getAgent() const { return agent; } + void setFederationLink(bool b); + /** Connection does not delete the listener. 0 resets. */ + void setErrorListener(ErrorListener* l) { errorListener=l; } + ErrorListener* getErrorListener() { return errorListener; } + + void setHeartbeatInterval(uint16_t heartbeat); + void sendHeartbeat(); + void restartTimeout(); + void abort(); + + template <class F> void eachSessionHandler(F f) { + for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i) + f(*ptr_map_ptr(i)); + } - // Extension points: allow plugins to insert additional functionality. - boost::function<void(framing::AMQFrame&)> receivedFn; - boost::function<void ()> closedFn; - boost::function<bool ()> doOutputFn; + void sendClose(); + void setSecureConnection(SecureConnection* secured); + + /** True if this is a shadow connection in a cluster. */ + bool isShadow() { return shadow; } + /** Called by cluster to mark shadow connections */ + void setShadow() { shadow = true; } + + // Used by cluster to update connection status + sys::AggregateOutput& getOutputTasks() { return outputTasks; } + + unsigned int getSSF() { return ssf; } private: typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap; typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; - void receivedImpl(framing::AMQFrame& frame); - void closedImpl(); - bool doOutputImpl(); - ChannelMap channels; - framing::AMQP_ClientProxy::Connection* client; + unsigned int ssf; ConnectionHandler adapter; - bool isLink; + const bool isLink; bool mgmtClosing; const std::string mgmtId; - boost::function0<void> ioCallback; - management::Connection* mgmtObject; + sys::Mutex ioCallbackLock; + std::queue<boost::function0<void> > ioCallbacks; + qmf::org::apache::qpid::broker::Connection* mgmtObject; LinkRegistry& links; + management::ManagementAgent* agent; + sys::Timer& timer; + boost::intrusive_ptr<sys::TimerTask> heartbeatTimer; + boost::intrusive_ptr<ConnectionTimeoutTask> timeoutTimer; + ErrorListener* errorListener; + bool shadow; + + public: + qmf::org::apache::qpid::broker::Connection* getMgmtObject() { return mgmtObject; } }; }} diff --git a/cpp/src/qpid/broker/ConnectionFactory.cpp b/cpp/src/qpid/broker/ConnectionFactory.cpp index 5de5a0230a..ffb0b34b95 100644 --- a/cpp/src/qpid/broker/ConnectionFactory.cpp +++ b/cpp/src/qpid/broker/ConnectionFactory.cpp @@ -18,30 +18,47 @@ * under the License. * */ -#include "ConnectionFactory.h" +#include "qpid/broker/ConnectionFactory.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/amqp_0_10/Connection.h" +#include "qpid/broker/Connection.h" +#include "qpid/log/Statement.h" namespace qpid { namespace broker { using framing::ProtocolVersion; +typedef std::auto_ptr<amqp_0_10::Connection> ConnectionPtr; +typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr; ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) {} ConnectionFactory::~ConnectionFactory() {} sys::ConnectionCodec* -ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id) { - if (v == ProtocolVersion(0, 10)) - return new amqp_0_10::Connection(out, broker, id); +ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, + unsigned int ) { + if (broker.getConnectionCounter().allowConnection()) + { + QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused"); + return 0; + } + if (v == ProtocolVersion(0, 10)) { + ConnectionPtr c(new amqp_0_10::Connection(out, id, false)); + c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, false))); + return c.release(); + } return 0; } sys::ConnectionCodec* -ConnectionFactory::create(sys::OutputControl& out, const std::string& id) { +ConnectionFactory::create(sys::OutputControl& out, const std::string& id, + unsigned int) { // used to create connections from one broker to another - return new amqp_0_10::Connection(out, broker, id, true); + ConnectionPtr c(new amqp_0_10::Connection(out, id, true)); + c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, true))); + return c.release(); } + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/ConnectionFactory.h b/cpp/src/qpid/broker/ConnectionFactory.h index 5797495054..d812292ad1 100644 --- a/cpp/src/qpid/broker/ConnectionFactory.h +++ b/cpp/src/qpid/broker/ConnectionFactory.h @@ -27,17 +27,20 @@ namespace qpid { namespace broker { class Broker; -class ConnectionFactory : public sys::ConnectionCodec::Factory { +class ConnectionFactory : public sys::ConnectionCodec::Factory +{ public: ConnectionFactory(Broker& b); virtual ~ConnectionFactory(); sys::ConnectionCodec* - create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id); + create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, + unsigned int conn_ssf); sys::ConnectionCodec* - create(sys::OutputControl&, const std::string& id); + create(sys::OutputControl&, const std::string& id, + unsigned int conn_ssf); private: Broker& broker; diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp index 77a4d1a3de..50a5aff2c9 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.cpp +++ b/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -20,70 +20,91 @@ * */ -#include "config.h" - -#include "ConnectionHandler.h" -#include "Connection.h" -#include "qpid/framing/ClientInvoker.h" -#include "qpid/framing/ServerInvoker.h" -#include "qpid/framing/constants.h" +#include "qpid/broker/ConnectionHandler.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/SecureConnection.h" +#include "qpid/Url.h" +#include "qpid/framing/AllInvoker.h" +#include "qpid/framing/enum.h" #include "qpid/log/Statement.h" +#include "qpid/sys/SecurityLayer.h" +#include "qpid/broker/AclModule.h" +#include "qmf/org/apache/qpid/broker/EventClientConnectFail.h" using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; +using qpid::sys::SecurityLayer; +namespace _qmf = qmf::org::apache::qpid::broker; - -namespace +namespace { const std::string ANONYMOUS = "ANONYMOUS"; const std::string PLAIN = "PLAIN"; const std::string en_US = "en_US"; +const std::string QPID_FED_LINK = "qpid.fed_link"; +const std::string QPID_FED_TAG = "qpid.federation_tag"; +const std::string SESSION_FLOW_CONTROL("qpid.session_flow"); +const std::string CLIENT_PROCESS_NAME("qpid.client_process"); +const std::string CLIENT_PID("qpid.client_pid"); +const std::string CLIENT_PPID("qpid.client_ppid"); +const int SESSION_FLOW_CONTROL_VER = 1; } -void ConnectionHandler::close(ReplyCode code, const string& text, ClassId, MethodId) +void ConnectionHandler::close(connection::CloseCode code, const string& text) { - handler->client.close(code, text); + handler->proxy.close(code, text); +} + +void ConnectionHandler::heartbeat() +{ + handler->proxy.heartbeat(); } void ConnectionHandler::handle(framing::AMQFrame& frame) { AMQMethodBody* method=frame.getBody()->getMethod(); + Connection::ErrorListener* errorListener = handler->connection.getErrorListener(); try{ - bool handled = false; - if (handler->serverMode) { - handled = invoke(static_cast<AMQP_ServerOperations::ConnectionHandler&>(*handler.get()), *method); - } else { - handled = invoke(static_cast<AMQP_ClientOperations::ConnectionHandler&>(*handler.get()), *method); - } - if (!handled) { + if (!invoke(static_cast<AMQP_AllOperations::ConnectionHandler&>(*handler.get()), *method)) { handler->connection.getChannel(frame.getChannel()).in(frame); } - }catch(ConnectionException& e){ - handler->client.close(e.code, e.what()); + if (errorListener) errorListener->connectionError(e.what()); + handler->proxy.close(e.code, e.what()); }catch(std::exception& e){ - handler->client.close(541/*internal error*/, e.what()); + if (errorListener) errorListener->connectionError(e.what()); + handler->proxy.close(541/*internal error*/, e.what()); } } +void ConnectionHandler::setSecureConnection(SecureConnection* secured) +{ + handler->secured = secured; +} + ConnectionHandler::ConnectionHandler(Connection& connection, bool isClient) : handler(new Handler(connection, isClient)) {} ConnectionHandler::Handler::Handler(Connection& c, bool isClient) : - client(c.getOutput()), server(c.getOutput()), - connection(c), serverMode(!isClient) + proxy(c.getOutput()), + connection(c), serverMode(!isClient), acl(0), secured(0) { if (serverMode) { + + acl = connection.getBroker().getAcl(); + FieldTable properties; Array mechanisms(0x95); - + + properties.setString(QPID_FED_TAG, connection.getBroker().getFederationTag()); + authenticator = SaslAuthenticator::createAuthenticator(c); authenticator->getMechanisms(mechanisms); - + Array locales(0x95); boost::shared_ptr<FieldValue> l(new Str16Value(en_US)); locales.add(l); - client.start(properties, mechanisms, locales); + proxy.start(properties, mechanisms, locales); } } @@ -91,65 +112,138 @@ ConnectionHandler::Handler::Handler(Connection& c, bool isClient) : ConnectionHandler::Handler::~Handler() {} -void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProperties*/, - const string& mechanism, +void ConnectionHandler::Handler::startOk(const framing::FieldTable& clientProperties, + const string& mechanism, const string& response, const string& /*locale*/) { - authenticator->start(mechanism, response); + try { + authenticator->start(mechanism, response); + } catch (std::exception& /*e*/) { + management::ManagementAgent* agent = connection.getAgent(); + if (agent) { + string error; + string uid; + authenticator->getError(error); + authenticator->getUid(uid); + agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error)); + } + throw; + } + connection.setFederationLink(clientProperties.get(QPID_FED_LINK)); + connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG)); + if (connection.isFederationLink()) { + if (acl && !acl->authorise(connection.getUserId(),acl::ACT_CREATE,acl::OBJ_LINK,"")){ + proxy.close(framing::connection::CLOSE_CODE_CONNECTION_FORCED,"ACL denied creating a federation link"); + return; + } + QPID_LOG(info, "Connection is a federation link"); + } + if (clientProperties.getAsInt(SESSION_FLOW_CONTROL) == SESSION_FLOW_CONTROL_VER) { + connection.setClientThrottling(); + } + + if (connection.getMgmtObject() != 0) { + string procName = clientProperties.getAsString(CLIENT_PROCESS_NAME); + uint32_t pid = clientProperties.getAsInt(CLIENT_PID); + uint32_t ppid = clientProperties.getAsInt(CLIENT_PPID); + + if (!procName.empty()) + connection.getMgmtObject()->set_remoteProcessName(procName); + if (pid != 0) + connection.getMgmtObject()->set_remotePid(pid); + if (ppid != 0) + connection.getMgmtObject()->set_remoteParentPid(ppid); + } } - + void ConnectionHandler::Handler::secureOk(const string& response) { - authenticator->step(response); + try { + authenticator->step(response); + } catch (std::exception& /*e*/) { + management::ManagementAgent* agent = connection.getAgent(); + if (agent) { + string error; + string uid; + authenticator->getError(error); + authenticator->getUid(uid); + agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error)); + } + throw; + } } - + void ConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/, uint16_t framemax, uint16_t heartbeat) { connection.setFrameMax(framemax); - connection.setHeartbeat(heartbeat); + connection.setHeartbeatInterval(heartbeat); } - + void ConnectionHandler::Handler::open(const string& /*virtualHost*/, const framing::Array& /*capabilities*/, bool /*insist*/) { - framing::Array knownhosts; - client.openOk(knownhosts); + std::vector<Url> urls = connection.broker.getKnownBrokers(); + framing::Array array(0x95); // str16 array + for (std::vector<Url>::iterator i = urls.begin(); i < urls.end(); ++i) + array.add(boost::shared_ptr<Str16Value>(new Str16Value(i->str()))); + proxy.openOk(array); + + //install security layer if one has been negotiated: + if (secured) { + std::auto_ptr<SecurityLayer> sl = authenticator->getSecurityLayer(connection.getFrameMax()); + if (sl.get()) secured->activateSecurityLayer(sl); + } } - + void ConnectionHandler::Handler::close(uint16_t replyCode, const string& replyText) { if (replyCode != 200) { QPID_LOG(warning, "Client closed connection with " << replyCode << ": " << replyText); } - if (replyCode == framing::connection::CONNECTION_FORCED) + if (replyCode == framing::connection::CLOSE_CODE_CONNECTION_FORCED) connection.notifyConnectionForced(replyText); - client.closeOk(); + proxy.closeOk(); connection.getOutput().close(); -} - +} + void ConnectionHandler::Handler::closeOk(){ connection.getOutput().close(); -} +} +void ConnectionHandler::Handler::heartbeat(){ + // For general case, do nothing - the purpose of heartbeats is + // just to make sure that there is some traffic on the connection + // within the heart beat interval, we check for the traffic and + // don't need to do anything in response to heartbeats. The + // exception is when we are in fact the client to another broker + // (i.e. an inter-broker link), in which case we echo the + // heartbeat back to the peer + if (!serverMode) proxy.heartbeat(); +} -void ConnectionHandler::Handler::start(const FieldTable& /*serverProperties*/, +void ConnectionHandler::Handler::start(const FieldTable& serverProperties, const framing::Array& /*mechanisms*/, const framing::Array& /*locales*/) { string mechanism = connection.getAuthMechanism(); string response = connection.getAuthCredentials(); - - server.startOk(FieldTable(), mechanism, response, en_US); + + connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG)); + + FieldTable ft; + ft.setInt(QPID_FED_LINK,1); + ft.setString(QPID_FED_TAG, connection.getBroker().getFederationTag()); + proxy.startOk(ft, mechanism, response, en_US); } void ConnectionHandler::Handler::secure(const string& /*challenge*/) { - server.secureOk(""); + proxy.secureOk(""); } void ConnectionHandler::Handler::tune(uint16_t channelMax, @@ -159,15 +253,19 @@ void ConnectionHandler::Handler::tune(uint16_t channelMax, { connection.setFrameMax(frameMax); connection.setHeartbeat(heartbeatMax); - server.tuneOk(channelMax, frameMax, heartbeatMax); - server.open("/", Array(), true); + proxy.tuneOk(channelMax, frameMax, heartbeatMax); + proxy.open("/", Array(), true); } -void ConnectionHandler::Handler::openOk(const framing::Array& /*knownHosts*/) +void ConnectionHandler::Handler::openOk(const framing::Array& knownHosts) { + for (Array::ValueVector::const_iterator i = knownHosts.begin(); i != knownHosts.end(); ++i) { + Url url((*i)->get<std::string>()); + connection.getKnownHosts().push_back(url); + } } void ConnectionHandler::Handler::redirect(const string& /*host*/, const framing::Array& /*knownHosts*/) { - + } diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h index a04936a943..d74f65da36 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.h +++ b/cpp/src/qpid/broker/ConnectionHandler.h @@ -22,68 +22,71 @@ #define _ConnectionAdapter_ #include <memory> -#include "SaslAuthenticator.h" +#include "qpid/broker/SaslAuthenticator.h" #include "qpid/framing/amqp_types.h" #include "qpid/framing/AMQFrame.h" -#include "qpid/framing/AMQP_ClientOperations.h" -#include "qpid/framing/AMQP_ClientProxy.h" -#include "qpid/framing/AMQP_ServerOperations.h" -#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/AMQP_AllOperations.h" +#include "qpid/framing/AMQP_AllProxy.h" +#include "qpid/framing/enum.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/ProtocolInitiation.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/Exception.h" +#include "qpid/broker/AclModule.h" namespace qpid { namespace broker { class Connection; +class SecureConnection; class ConnectionHandler : public framing::FrameHandler { - struct Handler : public framing::AMQP_ServerOperations::ConnectionHandler, - public framing::AMQP_ClientOperations::ConnectionHandler + struct Handler : public framing::AMQP_AllOperations::ConnectionHandler { - framing::AMQP_ClientProxy::Connection client; - framing::AMQP_ServerProxy::Connection server; + framing::AMQP_AllProxy::Connection proxy; Connection& connection; bool serverMode; std::auto_ptr<SaslAuthenticator> authenticator; - + AclModule* acl; + SecureConnection* secured; + Handler(Connection& connection, bool isClient); ~Handler(); void startOk(const qpid::framing::FieldTable& clientProperties, const std::string& mechanism, const std::string& response, - const std::string& locale); - void secureOk(const std::string& response); - void tuneOk(uint16_t channelMax, uint16_t frameMax, uint16_t heartbeat); - void heartbeat() {} + const std::string& locale); + void secureOk(const std::string& response); + void tuneOk(uint16_t channelMax, uint16_t frameMax, uint16_t heartbeat); + void heartbeat(); void open(const std::string& virtualHost, - const framing::Array& capabilities, bool insist); - void close(uint16_t replyCode, const std::string& replyText); - void closeOk(); + const framing::Array& capabilities, bool insist); + void close(uint16_t replyCode, const std::string& replyText); + void closeOk(); void start(const qpid::framing::FieldTable& serverProperties, const framing::Array& mechanisms, const framing::Array& locales); - + void secure(const std::string& challenge); - + void tune(uint16_t channelMax, uint16_t frameMax, uint16_t heartbeatMin, uint16_t heartbeatMax); - + void openOk(const framing::Array& knownHosts); - - void redirect(const std::string& host, const framing::Array& knownHosts); + + void redirect(const std::string& host, const framing::Array& knownHosts); }; std::auto_ptr<Handler> handler; public: ConnectionHandler(Connection& connection, bool isClient); - void close(framing::ReplyCode code, const std::string& text, framing::ClassId classId, framing::MethodId methodId); + void close(framing::connection::CloseCode code, const std::string& text); + void heartbeat(); void handle(framing::AMQFrame& frame); + void setSecureConnection(SecureConnection* secured); }; diff --git a/cpp/src/qpid/broker/ConnectionState.h b/cpp/src/qpid/broker/ConnectionState.h index c9cf6ece8d..77ac5a59b0 100644 --- a/cpp/src/qpid/broker/ConnectionState.h +++ b/cpp/src/qpid/broker/ConnectionState.h @@ -24,61 +24,97 @@ #include <vector> #include "qpid/sys/AggregateOutput.h" -#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/ConnectionOutputHandlerPtr.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/management/Manageable.h" -#include "Broker.h" +#include "qpid/Url.h" +#include "qpid/broker/Broker.h" namespace qpid { namespace broker { class ConnectionState : public ConnectionToken, public management::Manageable { + protected: + sys::ConnectionOutputHandlerPtr out; + public: - ConnectionState(qpid::sys::ConnectionOutputHandler* o, Broker& b) : - broker(b), - outputTasks(*o), - out(o), - framemax(65535), + ConnectionState(qpid::sys::ConnectionOutputHandler* o, Broker& b) : + out(o), + broker(b), + outputTasks(out), + framemax(65535), heartbeat(0), - stagingThreshold(broker.getStagingThreshold()) - {} - - + heartbeatmax(120), + stagingThreshold(broker.getStagingThreshold()), + federationLink(true), + clientSupportsThrottling(false), + clusterOrderOut(0) + {} virtual ~ConnectionState () {} uint32_t getFrameMax() const { return framemax; } uint16_t getHeartbeat() const { return heartbeat; } + uint16_t getHeartbeatMax() const { return heartbeatmax; } uint64_t getStagingThreshold() const { return stagingThreshold; } - void setFrameMax(uint32_t fm) { framemax = fm; } + void setFrameMax(uint32_t fm) { framemax = std::max(fm, (uint32_t) 4096); } void setHeartbeat(uint16_t hb) { heartbeat = hb; } + void setHeartbeatMax(uint16_t hbm) { heartbeatmax = hbm; } void setStagingThreshold(uint64_t st) { stagingThreshold = st; } virtual void setUserId(const string& uid) { userId = uid; } const string& getUserId() const { return userId; } + + void setUrl(const string& _url) { url = _url; } + const string& getUrl() const { return url; } + + void setFederationLink(bool b) { federationLink = b; } + bool isFederationLink() const { return federationLink; } + void setFederationPeerTag(const string& tag) { federationPeerTag = string(tag); } + const string& getFederationPeerTag() const { return federationPeerTag; } + std::vector<Url>& getKnownHosts() { return knownHosts; } + void setClientThrottling(bool set=true) { clientSupportsThrottling = set; } + bool getClientThrottling() const { return clientSupportsThrottling; } + Broker& getBroker() { return broker; } Broker& broker; std::vector<Queue::shared_ptr> exclusiveQueues; - + //contained output tasks sys::AggregateOutput outputTasks; - sys::ConnectionOutputHandler& getOutput() const { return *out; } + sys::ConnectionOutputHandler& getOutput() { return out; } framing::ProtocolVersion getVersion() const { return version; } + void setOutputHandler(qpid::sys::ConnectionOutputHandler* o) { out.set(o); } + + /** + * If the broker is part of a cluster, this is a handler provided + * by cluster code. It ensures consistent ordering of commands + * that are sent based on criteria that are not predictably + * ordered cluster-wide, e.g. a timer firing. + */ + framing::FrameHandler* getClusterOrderOutput() { return clusterOrderOut; } + void setClusterOrderOutput(framing::FrameHandler& fh) { clusterOrderOut = &fh; } - void setOutputHandler(qpid::sys::ConnectionOutputHandler* o) { out = o; } + virtual void requestIOProcessing (boost::function0<void>) = 0; protected: framing::ProtocolVersion version; - sys::ConnectionOutputHandler* out; uint32_t framemax; uint16_t heartbeat; + uint16_t heartbeatmax; uint64_t stagingThreshold; string userId; + string url; + bool federationLink; + string federationPeerTag; + std::vector<Url> knownHosts; + bool clientSupportsThrottling; + framing::FrameHandler* clusterOrderOut; }; }} diff --git a/cpp/src/qpid/broker/ConnectionToken.h b/cpp/src/qpid/broker/ConnectionToken.h index 0e3b301897..9b40383c80 100644 --- a/cpp/src/qpid/broker/ConnectionToken.h +++ b/cpp/src/qpid/broker/ConnectionToken.h @@ -21,7 +21,7 @@ #ifndef _ConnectionToken_ #define _ConnectionToken_ -#include "OwnershipToken.h" +#include "qpid/broker/OwnershipToken.h" namespace qpid { namespace broker { /** diff --git a/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h index 4274ce823e..b96443fa7c 100644 --- a/cpp/src/qpid/broker/Consumer.h +++ b/cpp/src/qpid/broker/Consumer.h @@ -21,47 +21,33 @@ #ifndef _Consumer_ #define _Consumer_ -namespace qpid { - namespace broker { - class Queue; -}} - -#include "Message.h" -#include "OwnershipToken.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/OwnershipToken.h" namespace qpid { - namespace broker { - - struct QueuedMessage - { - boost::intrusive_ptr<Message> payload; - framing::SequenceNumber position; - Queue* queue; - - QueuedMessage(Queue* q, boost::intrusive_ptr<Message> msg, framing::SequenceNumber sn) : - payload(msg), position(sn), queue(q) {} - QueuedMessage(Queue* q) : queue(q) {} - }; - +namespace broker { + +class Queue; + +class Consumer { + const bool acquires; + public: + typedef boost::shared_ptr<Consumer> shared_ptr; + + framing::SequenceNumber position; + + Consumer(bool preAcquires = true) : acquires(preAcquires) {} + bool preAcquires() const { return acquires; } + virtual bool deliver(QueuedMessage& msg) = 0; + virtual void notify() = 0; + virtual bool filter(boost::intrusive_ptr<Message>) { return true; } + virtual bool accept(boost::intrusive_ptr<Message>) { return true; } + virtual OwnershipToken* getSession() = 0; + virtual ~Consumer(){} +}; - class Consumer { - const bool acquires; - public: - typedef shared_ptr<Consumer> ptr; - - framing::SequenceNumber position; - - Consumer(bool preAcquires = true) : acquires(preAcquires) {} - bool preAcquires() const { return acquires; } - virtual bool deliver(QueuedMessage& msg) = 0; - virtual void notify() = 0; - virtual bool filter(boost::intrusive_ptr<Message>) { return true; } - virtual bool accept(boost::intrusive_ptr<Message>) { return true; } - virtual OwnershipToken* getSession() = 0; - virtual ~Consumer(){} - }; - } -} +}} #endif diff --git a/cpp/src/qpid/broker/Daemon.cpp b/cpp/src/qpid/broker/Daemon.cpp index c311730f76..b30e5f18cb 100644 --- a/cpp/src/qpid/broker/Daemon.cpp +++ b/cpp/src/qpid/broker/Daemon.cpp @@ -15,10 +15,16 @@ * limitations under the License. * */ -#include "Daemon.h" + +/* + * TODO: Note this is really a Posix specific implementation and so should be + * refactored together with windows/QpiddBroker into a more coherent daemon driver/ + * platform specific split + */ +#include "qpid/broker/Daemon.h" #include "qpid/log/Statement.h" #include "qpid/Exception.h" -#include "qpid/sys/LockFile.h" +#include "qpid/sys/posix/PidFile.h" #include <errno.h> #include <fcntl.h> @@ -31,7 +37,7 @@ namespace qpid { namespace broker { using namespace std; -using qpid::sys::LockFile; +using qpid::sys::PidFile; Daemon::Daemon(std::string _pidDir) : pidDir(_pidDir) { struct stat s; @@ -85,12 +91,13 @@ void Daemon::fork() child(); } catch (const exception& e) { - QPID_LOG(critical, "Daemon startup failed: " << e.what()); + QPID_LOG(critical, "Unexpected error: " << e.what()); uint16_t port = 0; - write(pipeFds[1], &port, sizeof(uint16_t)); + int unused_ret; //Supress warning about ignoring return value. + unused_ret = write(pipeFds[1], &port, sizeof(uint16_t)); std::string pipeFailureMessage = e.what(); - write ( pipeFds[1], + unused_ret = write ( pipeFds[1], pipeFailureMessage.c_str(), strlen(pipeFailureMessage.c_str()) ); @@ -108,52 +115,61 @@ Daemon::~Daemon() { } uint16_t Daemon::wait(int timeout) { // parent waits for child. - errno = 0; - struct timeval tv; - tv.tv_sec = timeout; - tv.tv_usec = 0; - - /* - * Rewritten using low-level IO, for compatibility - * with earlier Boost versions, i.e. 103200. - */ - fd_set fds; - FD_ZERO(&fds); - FD_SET(pipeFds[0], &fds); - int n=select(FD_SETSIZE, &fds, 0, 0, &tv); - if(n==0) throw Exception("Timed out waiting for daemon"); - if(n<0) throw ErrnoException("Error waiting for daemon"); - uint16_t port = 0; - /* - * Read the child's port number from the pipe. - */ - int desired_read = sizeof(uint16_t); - if ( desired_read > ::read(pipeFds[0], & port, desired_read) ) { - throw Exception("Cannot write lock file "+lockFile); + try { + errno = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + /* + * Rewritten using low-level IO, for compatibility + * with earlier Boost versions, i.e. 103200. + */ + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipeFds[0], &fds); + int n=select(FD_SETSIZE, &fds, 0, 0, &tv); + if(n==0) throw Exception("Timed out waiting for daemon (If store recovery is in progress, use longer wait time)"); + if(n<0) throw ErrnoException("Error waiting for daemon"); + uint16_t port = 0; + /* + * Read the child's port number from the pipe. + */ + int desired_read = sizeof(uint16_t); + if ( desired_read > ::read(pipeFds[0], & port, desired_read) ) + throw Exception("Cannot read from child process."); + + /* + * If the port number is 0, the child has put an error message + * on the pipe. Get it and throw it. + */ + if ( 0 == port ) { + // Skip whitespace + char c = ' '; + while ( isspace(c) ) { + if ( 1 > ::read(pipeFds[0], &c, 1) ) + throw Exception("Child port == 0, and no error message on pipe."); + } + + // Get Message + string errmsg; + do { + errmsg += c; + } while (::read(pipeFds[0], &c, 1)); + throw Exception("Daemon startup failed"+ + (errmsg.empty() ? string(".") : ": " + errmsg)); + } + return port; + } + catch (const std::exception& e) { + // Print directly to cerr. The caller will catch and log the + // exception, but in the case of a daemon parent process we + // also need to be sure the error goes to stderr. A + // dameon's logging configuration normally does not log to + // stderr. + std::cerr << e.what() << endl; + throw; } - - /* - * If the port number is 0, the child has put an error message - * on the pipe. Get it and throw it. - */ - if ( 0 == port ) { - // Skip whitespace - char c = ' '; - while ( isspace(c) ) { - if ( 1 > ::read(pipeFds[0], &c, 1) ) - throw Exception("Child port == 0, and no error message on pipe."); - } - - // Get Message - string errmsg; - do { - errmsg += c; - } while (::read(pipeFds[0], &c, 1)); - throw Exception("Daemon startup failed"+ - (errmsg.empty() ? string(".") : ": " + errmsg)); - } - - return port; } @@ -166,25 +182,17 @@ uint16_t Daemon::wait(int timeout) { // parent waits for child. */ void Daemon::ready(uint16_t port) { // child lockFile = pidFile(pidDir, port); - LockFile lf(lockFile, true); + PidFile lf(lockFile, true); /* - * Rewritten using low-level IO, for compatibility - * with earlier Boost versions, i.e. 103200. - */ - /* * Write the PID to the lockfile. */ - pid_t pid = getpid(); - int desired_write = sizeof(pid_t); - if ( desired_write > ::write(lf.fd, & pid, desired_write) ) { - throw Exception("Cannot write lock file "+lockFile); - } + lf.writePid(); /* * Write the port number to the parent. */ - desired_write = sizeof(uint16_t); + int desired_write = sizeof(uint16_t); if ( desired_write > ::write(pipeFds[1], & port, desired_write) ) { throw Exception("Error writing to parent." ); } @@ -198,17 +206,8 @@ void Daemon::ready(uint16_t port) { // child */ pid_t Daemon::getPid(string _pidDir, uint16_t port) { string name = pidFile(_pidDir, port); - LockFile lf(name, false); - pid_t pid; - - /* - * Rewritten using low-level IO, for compatibility - * with earlier Boost versions, i.e. 103200. - */ - int desired_read = sizeof(pid_t); - if ( desired_read > ::read(lf.fd, & pid, desired_read) ) { - throw Exception("Cannot read lock file " + name); - } + PidFile lf(name, false); + pid_t pid = lf.readPid(); if (kill(pid, 0) < 0 && errno != EPERM) { unlink(name.c_str()); throw Exception("Removing stale lock file "+name); diff --git a/cpp/src/qpid/broker/Daemon.h b/cpp/src/qpid/broker/Daemon.h index 98468debb7..a9cd98bce2 100644 --- a/cpp/src/qpid/broker/Daemon.h +++ b/cpp/src/qpid/broker/Daemon.h @@ -19,10 +19,12 @@ * */ -#include <string> +#include "qpid/sys/IntegerTypes.h" #include <boost/scoped_ptr.hpp> #include <boost/function.hpp> #include <boost/noncopyable.hpp> +#include <string> + namespace qpid { namespace broker { diff --git a/cpp/src/qpid/broker/Deliverable.h b/cpp/src/qpid/broker/Deliverable.h index c40780c4ae..433469a212 100644 --- a/cpp/src/qpid/broker/Deliverable.h +++ b/cpp/src/qpid/broker/Deliverable.h @@ -21,8 +21,8 @@ #ifndef _Deliverable_ #define _Deliverable_ -#include "Queue.h" -#include "Message.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/Message.h" namespace qpid { namespace broker { @@ -33,7 +33,7 @@ namespace qpid { virtual Message& getMessage() = 0; - virtual void deliverTo(Queue::shared_ptr& queue) = 0; + virtual void deliverTo(const boost::shared_ptr<Queue>& queue) = 0; virtual uint64_t contentSize() { return 0; } virtual ~Deliverable(){} }; diff --git a/cpp/src/qpid/broker/DeliverableMessage.cpp b/cpp/src/qpid/broker/DeliverableMessage.cpp index fd15acf464..658e6bf48f 100644 --- a/cpp/src/qpid/broker/DeliverableMessage.cpp +++ b/cpp/src/qpid/broker/DeliverableMessage.cpp @@ -18,15 +18,15 @@ * under the License. * */ -#include "DeliverableMessage.h" +#include "qpid/broker/DeliverableMessage.h" using namespace qpid::broker; -DeliverableMessage::DeliverableMessage(boost::intrusive_ptr<Message>& _msg) : msg(_msg) +DeliverableMessage::DeliverableMessage(const boost::intrusive_ptr<Message>& _msg) : msg(_msg) { } -void DeliverableMessage::deliverTo(Queue::shared_ptr& queue) +void DeliverableMessage::deliverTo(const boost::shared_ptr<Queue>& queue) { queue->deliver(msg); delivered = true; diff --git a/cpp/src/qpid/broker/DeliverableMessage.h b/cpp/src/qpid/broker/DeliverableMessage.h index 18e1ec5e29..08abce35ef 100644 --- a/cpp/src/qpid/broker/DeliverableMessage.h +++ b/cpp/src/qpid/broker/DeliverableMessage.h @@ -21,9 +21,10 @@ #ifndef _DeliverableMessage_ #define _DeliverableMessage_ -#include "Deliverable.h" -#include "Queue.h" -#include "Message.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/Message.h" #include <boost/intrusive_ptr.hpp> @@ -32,10 +33,10 @@ namespace qpid { class DeliverableMessage : public Deliverable{ boost::intrusive_ptr<Message> msg; public: - DeliverableMessage(boost::intrusive_ptr<Message>& msg); - virtual void deliverTo(Queue::shared_ptr& queue); - Message& getMessage(); - uint64_t contentSize(); + QPID_BROKER_EXTERN DeliverableMessage(const boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue); + QPID_BROKER_EXTERN Message& getMessage(); + QPID_BROKER_EXTERN uint64_t contentSize(); virtual ~DeliverableMessage(){} }; } diff --git a/cpp/src/qpid/broker/DeliveryAdapter.h b/cpp/src/qpid/broker/DeliveryAdapter.h index 4c2b2f615f..b0bec60890 100644 --- a/cpp/src/qpid/broker/DeliveryAdapter.h +++ b/cpp/src/qpid/broker/DeliveryAdapter.h @@ -21,30 +21,31 @@ #ifndef _DeliveryAdapter_ #define _DeliveryAdapter_ -#include "DeliveryId.h" -#include "DeliveryToken.h" -#include "Message.h" +#include "qpid/broker/DeliveryId.h" +#include "qpid/broker/Message.h" #include "qpid/framing/amqp_types.h" namespace qpid { namespace broker { - /** - * The intention behind this interface is to separate the generic - * handling of some form of message delivery to clients that is - * contained in the version independent Channel class from the - * details required for a particular situation or - * version. i.e. where the existing adapters allow (through - * supporting the generated interface for a version of the - * protocol) inputs of a channel to be adapted to the version - * independent part, this does the same for the outputs. - */ - class DeliveryAdapter - { - public: - virtual DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) = 0; - virtual ~DeliveryAdapter(){} - }; +class DeliveryRecord; + +/** + * The intention behind this interface is to separate the generic + * handling of some form of message delivery to clients that is + * contained in the version independent Channel class from the + * details required for a particular situation or + * version. i.e. where the existing adapters allow (through + * supporting the generated interface for a version of the + * protocol) inputs of a channel to be adapted to the version + * independent part, this does the same for the outputs. + */ +class DeliveryAdapter +{ + public: + virtual void deliver(DeliveryRecord&, bool sync) = 0; + virtual ~DeliveryAdapter(){} +}; }} diff --git a/cpp/src/qpid/broker/DeliveryRecord.cpp b/cpp/src/qpid/broker/DeliveryRecord.cpp index 530dca99a4..22ec5e86a0 100644 --- a/cpp/src/qpid/broker/DeliveryRecord.cpp +++ b/cpp/src/qpid/broker/DeliveryRecord.cpp @@ -18,91 +18,72 @@ * under the License. * */ -#include "DeliveryRecord.h" -#include "DeliverableMessage.h" -#include "SemanticState.h" -#include "Exchange.h" +#include "qpid/broker/DeliveryRecord.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/SemanticState.h" +#include "qpid/broker/Exchange.h" #include "qpid/log/Statement.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/MessageTransferBody.h" +using namespace qpid; using namespace qpid::broker; using std::string; DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg, - Queue::shared_ptr _queue, - const std::string _tag, - DeliveryToken::shared_ptr _token, - const DeliveryId _id, - bool _acquired, bool accepted) : msg(_msg), - queue(_queue), - tag(_tag), - token(_token), - id(_id), - acquired(_acquired), - pull(false), - cancelled(false), - credit(msg.payload ? msg.payload->getRequiredCredit() : 0), - size(msg.payload ? msg.payload->contentSize() : 0), - completed(false), - ended(accepted) -{ - if (accepted) setEnded(); -} - -DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg, - Queue::shared_ptr _queue, - const DeliveryId _id) : msg(_msg), - queue(_queue), - id(_id), - acquired(true), - pull(true), - cancelled(false), - credit(msg.payload ? msg.payload->getRequiredCredit() : 0), - size(msg.payload ? msg.payload->contentSize() : 0), - completed(false), - ended(false) + const Queue::shared_ptr& _queue, + const std::string& _tag, + bool _acquired, + bool accepted, + bool _windowing, + uint32_t _credit) : msg(_msg), + queue(_queue), + tag(_tag), + acquired(_acquired), + acceptExpected(!accepted), + cancelled(false), + completed(false), + ended(accepted && acquired), + windowing(_windowing), + credit(msg.payload ? msg.payload->getRequiredCredit() : _credit) {} -void DeliveryRecord::setEnded() +bool DeliveryRecord::setEnded() { ended = true; //reset msg pointer, don't need to hold on to it anymore msg.payload = boost::intrusive_ptr<Message>(); - QPID_LOG(debug, "DeliveryRecord::setEnded() id=" << id); -} - -bool DeliveryRecord::matches(DeliveryId tag) const{ - return id == tag; -} - -bool DeliveryRecord::matchOrAfter(DeliveryId tag) const{ - return matches(tag) || after(tag); -} - -bool DeliveryRecord::after(DeliveryId tag) const{ - return id > tag; -} - -bool DeliveryRecord::coveredBy(const framing::SequenceSet* const range) const{ - return range->contains(id); + return isRedundant(); } void DeliveryRecord::redeliver(SemanticState* const session) { if (!ended) { - if(pull || cancelled){ - //if message was originally sent as response to get, we must requeue it - - //or if subscription was cancelled, requeue it (waiting for + if(cancelled){ + //if subscription was cancelled, requeue it (waiting for //final confirmation for AMQP WG on this case) - requeue(); }else{ msg.payload->redeliver();//mark as redelivered - id = session->redeliver(msg, token); + session->deliver(*this, false); } } } +void DeliveryRecord::deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize) +{ + id = deliveryId; + if (msg.payload->getRedelivered()){ + msg.payload->getProperties<framing::DeliveryProperties>()->setRedelivered(true); + } + + framing::AMQFrame method((framing::MessageTransferBody(framing::ProtocolVersion(), tag, acceptExpected ? 0 : 1, acquired ? 0 : 1))); + method.setEof(false); + h.handle(method); + msg.payload->sendHeader(h, framesize); + msg.payload->sendContent(*queue, h, framesize); +} + void DeliveryRecord::requeue() const { if (acquired && !ended) { @@ -123,25 +104,29 @@ void DeliveryRecord::release(bool setRedelivered) } } -void DeliveryRecord::complete() -{ +void DeliveryRecord::complete() { completed = true; } -void DeliveryRecord::accept(TransactionContext* ctxt) { +bool DeliveryRecord::accept(TransactionContext* ctxt) { if (acquired && !ended) { - queue->dequeue(ctxt, msg.payload); + queue->dequeue(ctxt, msg); setEnded(); QPID_LOG(debug, "Accepted " << id); } + return isRedundant(); } void DeliveryRecord::dequeue(TransactionContext* ctxt) const{ if (acquired && !ended) { - queue->dequeue(ctxt, msg.payload); + queue->dequeue(ctxt, msg); } } +void DeliveryRecord::committed() const{ + queue->dequeueCommitted(msg); +} + void DeliveryRecord::reject() { Exchange::shared_ptr alternate = queue->getAlternateExchange(); @@ -161,29 +146,14 @@ uint32_t DeliveryRecord::getCredit() const return credit; } - -void DeliveryRecord::addTo(Prefetch& prefetch) const{ - if(!pull){ - //ignore 'pulled' messages (i.e. those that were sent in - //response to get) when calculating prefetch - prefetch.size += size; - prefetch.count++; - } -} - -void DeliveryRecord::subtractFrom(Prefetch& prefetch) const{ - if(!pull){ - //ignore 'pulled' messages (i.e. those that were sent in - //response to get) when calculating prefetch - prefetch.size -= size; - prefetch.count--; - } -} - void DeliveryRecord::acquire(DeliveryIds& results) { if (queue->acquire(msg)) { acquired = true; results.push_back(id); + if (!acceptExpected) { + if (ended) { QPID_LOG(error, "Can't dequeue ended message"); } + else { queue->dequeue(0, msg); setEnded(); } + } } else { QPID_LOG(info, "Message already acquired " << id.getValue()); } @@ -195,6 +165,16 @@ void DeliveryRecord::cancel(const std::string& cancelledTag) cancelled = true; } +AckRange DeliveryRecord::findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last) +{ + DeliveryRecords::iterator start = lower_bound(records.begin(), records.end(), first); + // Find end - position it just after the last record in range + DeliveryRecords::iterator end = lower_bound(records.begin(), records.end(), last); + if (end != records.end() && end->getId() == last) ++end; + return AckRange(start, end); +} + + namespace qpid { namespace broker { @@ -206,9 +186,5 @@ std::ostream& operator<<(std::ostream& out, const DeliveryRecord& r) return out; } -bool operator<(const DeliveryRecord& a, const DeliveryRecord& b) -{ - return a.id < b.id; -} }} diff --git a/cpp/src/qpid/broker/DeliveryRecord.h b/cpp/src/qpid/broker/DeliveryRecord.h index 78dc99e3c6..5f802766b6 100644 --- a/cpp/src/qpid/broker/DeliveryRecord.h +++ b/cpp/src/qpid/broker/DeliveryRecord.h @@ -1,3 +1,6 @@ +#ifndef QPID_BROKER_DELIVERYRECORD_H +#define QPID_BROKER_DELIVERYRECORD_H + /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -18,53 +21,60 @@ * under the License. * */ -#ifndef _DeliveryRecord_ -#define _DeliveryRecord_ #include <algorithm> -#include <list> +#include <deque> #include <vector> #include <ostream> #include "qpid/framing/SequenceSet.h" -#include "Queue.h" -#include "Consumer.h" -#include "DeliveryId.h" -#include "DeliveryToken.h" -#include "Message.h" -#include "Prefetch.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/QueuedMessage.h" +#include "qpid/broker/DeliveryId.h" +#include "qpid/broker/Message.h" namespace qpid { namespace broker { class SemanticState; +struct AckRange; /** * Record of a delivery for which an ack is outstanding. */ -class DeliveryRecord{ +class DeliveryRecord +{ QueuedMessage msg; mutable Queue::shared_ptr queue; - const std::string tag; - DeliveryToken::shared_ptr token; + std::string tag; DeliveryId id; - bool acquired; - const bool pull; - bool cancelled; - const uint32_t credit; - const uint64_t size; - - bool completed; - bool ended; + bool acquired : 1; + bool acceptExpected : 1; + bool cancelled : 1; + bool completed : 1; + bool ended : 1; + bool windowing : 1; + + /** + * Record required credit on construction as the pointer to the + * message may be reset once we no longer need to deliver it + * (e.g. when it is accepted), but we will still need to be able + * to reallocate credit when it is completed (which could happen + * after that). + */ + uint32_t credit; public: - DeliveryRecord(const QueuedMessage& msg, Queue::shared_ptr queue, const std::string tag, DeliveryToken::shared_ptr token, - const DeliveryId id, bool acquired, bool confirmed = false); - DeliveryRecord(const QueuedMessage& msg, Queue::shared_ptr queue, const DeliveryId id); - - bool matches(DeliveryId tag) const; - bool matchOrAfter(DeliveryId tag) const; - bool after(DeliveryId tag) const; - bool coveredBy(const framing::SequenceSet* const range) const; - + QPID_BROKER_EXTERN DeliveryRecord(const QueuedMessage& msg, + const Queue::shared_ptr& queue, + const std::string& tag, + bool acquired, + bool accepted, + bool windowing, + uint32_t credit=0 // Only used if msg is empty. + ); + + bool coveredBy(const framing::SequenceSet* const range) const { return range->contains(id); } + void dequeue(TransactionContext* ctxt = 0) const; void requeue() const; void release(bool setRedelivered); @@ -73,32 +83,37 @@ class DeliveryRecord{ void redeliver(SemanticState* const); void acquire(DeliveryIds& results); void complete(); - void accept(TransactionContext* ctxt); - void setEnded(); + bool accept(TransactionContext* ctxt); // Returns isRedundant() + bool setEnded(); // Returns isRedundant() + void committed() const; bool isAcquired() const { return acquired; } bool isComplete() const { return completed; } - bool isRedundant() const { return ended && completed; } - + bool isRedundant() const { return ended && (!windowing || completed); } + bool isCancelled() const { return cancelled; } + bool isAccepted() const { return !acceptExpected; } + bool isEnded() const { return ended; } + bool isWindowing() const { return windowing; } + uint32_t getCredit() const; - void addTo(Prefetch&) const; - void subtractFrom(Prefetch&) const; - const std::string& getTag() const { return tag; } - bool isPull() const { return pull; } - friend bool operator<(const DeliveryRecord&, const DeliveryRecord&); - friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&); -}; + const std::string& getTag() const { return tag; } -typedef std::list<DeliveryRecord> DeliveryRecords; -typedef std::list<DeliveryRecord>::iterator ack_iterator; + void deliver(framing::FrameHandler& h, DeliveryId deliveryId, uint16_t framesize); + void setId(DeliveryId _id) { id = _id; } -struct AckRange -{ - ack_iterator start; - ack_iterator end; - AckRange(ack_iterator _start, ack_iterator _end) : start(_start), end(_end) {} + typedef std::deque<DeliveryRecord> DeliveryRecords; + static AckRange findRange(DeliveryRecords& records, DeliveryId first, DeliveryId last); + const QueuedMessage& getMessage() const { return msg; } + framing::SequenceNumber getId() const { return id; } + Queue::shared_ptr getQueue() const { return queue; } + + friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&); }; +inline bool operator<(const DeliveryRecord& a, const DeliveryRecord& b) { return a.getId() < b.getId(); } +inline bool operator<(const framing::SequenceNumber& a, const DeliveryRecord& b) { return a < b.getId(); } +inline bool operator<(const DeliveryRecord& a, const framing::SequenceNumber& b) { return a.getId() < b; } + struct AcquireFunctor { DeliveryIds& results; @@ -111,8 +126,17 @@ struct AcquireFunctor } }; +typedef DeliveryRecord::DeliveryRecords DeliveryRecords; + +struct AckRange +{ + DeliveryRecords::iterator start; + DeliveryRecords::iterator end; + AckRange(DeliveryRecords::iterator _start, DeliveryRecords::iterator _end) : start(_start), end(_end) {} +}; + } } -#endif +#endif /*!QPID_BROKER_DELIVERYRECORD_H*/ diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp index 4aa68bee9c..094f59cdec 100644 --- a/cpp/src/qpid/broker/DirectExchange.cpp +++ b/cpp/src/qpid/broker/DirectExchange.cpp @@ -19,111 +19,142 @@ * */ #include "qpid/log/Statement.h" -#include "DirectExchange.h" +#include "qpid/broker/DirectExchange.h" #include <iostream> using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; using qpid::management::Manageable; +namespace _qmf = qmf::org::apache::qpid::broker; -DirectExchange::DirectExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); +const std::string qpidExclusiveBinding("qpid.exclusive-binding"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + +DirectExchange::DirectExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b) { if (mgmtExchange != 0) - mgmtExchange->set_type (typeName); + mgmtExchange->set_type(typeName); } -DirectExchange::DirectExchange(const std::string& _name, bool _durable, - const FieldTable& _args, Manageable* _parent) : - Exchange(_name, _durable, _args, _parent) +DirectExchange::DirectExchange(const string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent, Broker* b) : + Exchange(_name, _durable, _args, _parent, b) { if (mgmtExchange != 0) - mgmtExchange->set_type (typeName); + mgmtExchange->set_type(typeName); } -bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable*){ - RWlock::ScopedWlock l(lock); - std::vector<Binding::shared_ptr>& queues(bindings[routingKey]); - std::vector<Binding::shared_ptr>::iterator i; - - for (i = queues.begin(); i != queues.end(); i++) - if ((*i)->queue == queue) - break; - - if (i == queues.end()) { - Binding::shared_ptr binding (new Binding (routingKey, queue, this)); - bindings[routingKey].push_back(binding); - if (mgmtExchange != 0) { - mgmtExchange->inc_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->inc_bindingCount(); - } - return true; - } else{ - return false; +bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args) +{ + string fedOp(fedOpBind); + string fedTags; + string fedOrigin; + bool exclusiveBinding = false; + if (args) { + fedOp = args->getAsString(qpidFedOp); + fedTags = args->getAsString(qpidFedTags); + fedOrigin = args->getAsString(qpidFedOrigin); + exclusiveBinding = args->get(qpidExclusiveBinding); } -} -bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ - RWlock::ScopedWlock l(lock); - std::vector<Binding::shared_ptr>& queues(bindings[routingKey]); - std::vector<Binding::shared_ptr>::iterator i; + bool propagate = false; - for (i = queues.begin(); i != queues.end(); i++) - if ((*i)->queue == queue) - break; + if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { + Mutex::ScopedLock l(lock); + Binding::shared_ptr b(new Binding(routingKey, queue, this, FieldTable(), fedOrigin)); + BoundKey& bk = bindings[routingKey]; + if (exclusiveBinding) bk.queues.clear(); - if (i < queues.end()) { - queues.erase(i); - if (queues.empty()) { - bindings.erase(routingKey); + if (bk.queues.add_unless(b, MatchQueue(queue))) { + propagate = bk.fedBinding.addOrigin(fedOrigin); + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + } + } else { + return false; } - if (mgmtExchange != 0) { - mgmtExchange->dec_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->dec_bindingCount(); + } else if (fedOp == fedOpUnbind) { + Mutex::ScopedLock l(lock); + BoundKey& bk = bindings[routingKey]; + propagate = bk.fedBinding.delOrigin(fedOrigin); + if (bk.fedBinding.count() == 0) + unbind(queue, routingKey, 0); + } else if (fedOp == fedOpReorigin) { + /** gather up all the keys that need rebinding in a local vector + * while holding the lock. Then propagate once the lock is + * released + */ + std::vector<std::string> keys2prop; + { + Mutex::ScopedLock l(lock); + for (Bindings::iterator iter = bindings.begin(); + iter != bindings.end(); iter++) { + const BoundKey& bk = iter->second; + if (bk.fedBinding.hasLocal()) { + keys2prop.push_back(iter->first); + } + } + } /* lock dropped */ + for (std::vector<std::string>::const_iterator key = keys2prop.begin(); + key != keys2prop.end(); key++) { + propagateFedOp( *key, string(), fedOpBind, string()); } - return true; - } else { - return false; } + + routeIVE(); + if (propagate) + propagateFedOp(routingKey, fedTags, fedOp, fedOrigin); + return true; } -void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ - RWlock::ScopedRlock l(lock); - std::vector<Binding::shared_ptr>& queues(bindings[routingKey]); - std::vector<Binding::shared_ptr>::iterator i; - int count(0); - - for(i = queues.begin(); i != queues.end(); i++, count++) { - msg.deliverTo((*i)->queue); - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched (); - } - - if(!count){ - QPID_LOG(warning, "DirectExchange " << getName() << " could not route message with key " << routingKey); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - } - else { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); +bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/) +{ + bool propagate = false; + + { + Mutex::ScopedLock l(lock); + BoundKey& bk = bindings[routingKey]; + if (bk.queues.remove_if(MatchQueue(queue))) { + propagate = bk.fedBinding.delOrigin(); + if (mgmtExchange != 0) { + mgmtExchange->dec_bindingCount(); + } + } else { + return false; } } - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (propagate) + propagateFedOp(routingKey, string(), fedOpUnbind, string()); + return true; +} + +void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +{ + PreRoute pr(msg, this); + ConstBindingList b; + { + Mutex::ScopedLock l(lock); + b = bindings[routingKey].queues.snapshot(); } + doRoute(msg, b); } bool DirectExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) { - std::vector<Binding::shared_ptr>::iterator j; - + Mutex::ScopedLock l(lock); if (routingKey) { Bindings::iterator i = bindings.find(*routingKey); @@ -131,17 +162,17 @@ bool DirectExchange::isBound(Queue::shared_ptr queue, const string* const routin return false; if (!queue) return true; - for (j = i->second.begin(); j != i->second.end(); j++) - if ((*j)->queue == queue) - return true; + + Queues::ConstPtr p = i->second.queues.snapshot(); + return p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end(); } else if (!queue) { //if no queue or routing key is specified, just report whether any bindings exist return bindings.size() > 0; } else { - for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) - for (j = i->second.begin(); j != i->second.end(); j++) - if ((*j)->queue == queue) - return true; + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) { + Queues::ConstPtr p = i->second.queues.snapshot(); + if (p && std::find_if(p->begin(), p->end(), MatchQueue(queue)) != p->end()) return true; + } return false; } diff --git a/cpp/src/qpid/broker/DirectExchange.h b/cpp/src/qpid/broker/DirectExchange.h index 118f2ed4d3..9a73f3bc41 100644 --- a/cpp/src/qpid/broker/DirectExchange.h +++ b/cpp/src/qpid/broker/DirectExchange.h @@ -23,40 +23,53 @@ #include <map> #include <vector> -#include "Exchange.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Exchange.h" #include "qpid/framing/FieldTable.h" -#include "qpid/sys/Monitor.h" -#include "Queue.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "qpid/sys/Mutex.h" +#include "qpid/broker/Queue.h" namespace qpid { namespace broker { - class DirectExchange : public virtual Exchange{ - typedef std::vector<Binding::shared_ptr> Queues; - typedef std::map<string, Queues> Bindings; - Bindings bindings; - qpid::sys::RWlock lock; - - public: - static const std::string typeName; - - DirectExchange(const std::string& name, management::Manageable* parent = 0); - DirectExchange(const string& _name, bool _durable, - const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); +class DirectExchange : public virtual Exchange { + typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Queues; + struct BoundKey { + Queues queues; + FedBinding fedBinding; + }; + typedef std::map<string, BoundKey> Bindings; + Bindings bindings; + qpid::sys::Mutex lock; - virtual std::string getType() const { return typeName; } +public: + static const std::string typeName; - virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - - virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN DirectExchange(const std::string& name, + management::Manageable* parent = 0, Broker* broker = 0); + QPID_BROKER_EXTERN DirectExchange(const string& _name, + bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0, Broker* broker = 0); - virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + virtual std::string getType() const { return typeName; } + + QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue, + const std::string& routingKey, + const qpid::framing::FieldTable* args); + 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 bool isBound(Queue::shared_ptr queue, + const string* const routingKey, + const qpid::framing::FieldTable* const args); - virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + QPID_BROKER_EXTERN virtual ~DirectExchange(); - virtual ~DirectExchange(); - }; -} -} + virtual bool supportsDynamicBinding() { return true; } +}; +}} #endif diff --git a/cpp/src/qpid/broker/DtxAck.cpp b/cpp/src/qpid/broker/DtxAck.cpp index 47637369ca..bca3f90bbe 100644 --- a/cpp/src/qpid/broker/DtxAck.cpp +++ b/cpp/src/qpid/broker/DtxAck.cpp @@ -18,7 +18,7 @@ * under the License. * */ -#include "DtxAck.h" +#include "qpid/broker/DtxAck.h" #include "qpid/log/Statement.h" using std::bind1st; @@ -26,7 +26,7 @@ using std::bind2nd; using std::mem_fun_ref; using namespace qpid::broker; -DtxAck::DtxAck(const framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked) +DtxAck::DtxAck(const qpid::framing::SequenceSet& acked, DeliveryRecords& unacked) { remove_copy_if(unacked.begin(), unacked.end(), inserter(pending, pending.end()), not1(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked))); @@ -36,7 +36,7 @@ bool DtxAck::prepare(TransactionContext* ctxt) throw() { try{ //record dequeue in the store - for (ack_iterator i = pending.begin(); i != pending.end(); i++) { + for (DeliveryRecords::iterator i = pending.begin(); i != pending.end(); i++) { i->dequeue(ctxt); } return true; @@ -48,11 +48,26 @@ bool DtxAck::prepare(TransactionContext* ctxt) throw() void DtxAck::commit() throw() { - pending.clear(); + try { + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::committed)); + pending.clear(); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to commit (unknown error)"); + } + } void DtxAck::rollback() throw() { - for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue)); - pending.clear(); + try { + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue)); + pending.clear(); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to complete rollback: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to complete rollback (unknown error)"); + } + } diff --git a/cpp/src/qpid/broker/DtxAck.h b/cpp/src/qpid/broker/DtxAck.h index 05c4499839..166147e58d 100644 --- a/cpp/src/qpid/broker/DtxAck.h +++ b/cpp/src/qpid/broker/DtxAck.h @@ -25,20 +25,21 @@ #include <functional> #include <list> #include "qpid/framing/SequenceSet.h" -#include "DeliveryRecord.h" -#include "TxOp.h" +#include "qpid/broker/DeliveryRecord.h" +#include "qpid/broker/TxOp.h" namespace qpid { namespace broker { class DtxAck : public TxOp{ - std::list<DeliveryRecord> pending; + DeliveryRecords pending; public: - DtxAck(const framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked); + DtxAck(const framing::SequenceSet& acked, DeliveryRecords& unacked); virtual bool prepare(TransactionContext* ctxt) throw(); virtual void commit() throw(); virtual void rollback() throw(); virtual ~DtxAck(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } }; } } diff --git a/cpp/src/qpid/broker/DtxBuffer.cpp b/cpp/src/qpid/broker/DtxBuffer.cpp index 29a07ea6d9..f1b8169cf7 100644 --- a/cpp/src/qpid/broker/DtxBuffer.cpp +++ b/cpp/src/qpid/broker/DtxBuffer.cpp @@ -18,7 +18,7 @@ * under the License. * */ -#include "DtxBuffer.h" +#include "qpid/broker/DtxBuffer.h" using namespace qpid::broker; using qpid::sys::Mutex; diff --git a/cpp/src/qpid/broker/DtxBuffer.h b/cpp/src/qpid/broker/DtxBuffer.h index b302632037..1511cb032f 100644 --- a/cpp/src/qpid/broker/DtxBuffer.h +++ b/cpp/src/qpid/broker/DtxBuffer.h @@ -21,7 +21,8 @@ #ifndef _DtxBuffer_ #define _DtxBuffer_ -#include "TxBuffer.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/TxBuffer.h" #include "qpid/sys/Mutex.h" namespace qpid { @@ -37,9 +38,9 @@ namespace qpid { public: typedef boost::shared_ptr<DtxBuffer> shared_ptr; - DtxBuffer(const std::string& xid = ""); - ~DtxBuffer(); - void markEnded(); + QPID_BROKER_EXTERN DtxBuffer(const std::string& xid = ""); + QPID_BROKER_EXTERN ~DtxBuffer(); + QPID_BROKER_EXTERN void markEnded(); bool isEnded(); void setSuspended(bool suspended); bool isSuspended(); diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp index 942dbdcbc6..a2ab20ec44 100644 --- a/cpp/src/qpid/broker/DtxManager.cpp +++ b/cpp/src/qpid/broker/DtxManager.cpp @@ -18,10 +18,11 @@ * under the License. * */ -#include "DtxManager.h" -#include "DtxTimeout.h" +#include "qpid/broker/DtxManager.h" +#include "qpid/broker/DtxTimeout.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" +#include "qpid/sys/Timer.h" #include "qpid/ptr_map.h" #include <boost/format.hpp> @@ -33,7 +34,7 @@ using qpid::ptr_map_ptr; using namespace qpid::broker; using namespace qpid::framing; -DtxManager::DtxManager() : store(0) {} +DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(t) {} DtxManager::~DtxManager() {} @@ -126,12 +127,11 @@ void DtxManager::setTimeout(const std::string& xid, uint32_t secs) intrusive_ptr<DtxTimeout> timeout = record->getTimeout(); if (timeout.get()) { if (timeout->timeout == secs) return;//no need to do anything further if timeout hasn't changed - timeout->cancelled = true; + timeout->cancel(); } timeout = intrusive_ptr<DtxTimeout>(new DtxTimeout(secs, *this, xid)); record->setTimeout(timeout); - timer.add(boost::static_pointer_cast<TimerTask>(timeout)); - + timer.add(timeout); } uint32_t DtxManager::getTimeout(const std::string& xid) @@ -160,13 +160,12 @@ void DtxManager::DtxCleanup::fire() { try { mgr.remove(xid); - } catch (ConnectionException& e) { + } catch (ConnectionException& /*e*/) { //assume it was explicitly cleaned up after a call to prepare, commit or rollback } } void DtxManager::setStore (TransactionalStore* _store) { - assert (store == 0 && _store != 0); store = _store; } diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h index fa5c62c233..680b62eeb2 100644 --- a/cpp/src/qpid/broker/DtxManager.h +++ b/cpp/src/qpid/broker/DtxManager.h @@ -22,11 +22,11 @@ #define _DtxManager_ #include <boost/ptr_container/ptr_map.hpp> -#include "DtxBuffer.h" -#include "DtxWorkRecord.h" -#include "Timer.h" -#include "TransactionalStore.h" +#include "qpid/broker/DtxBuffer.h" +#include "qpid/broker/DtxWorkRecord.h" +#include "qpid/broker/TransactionalStore.h" #include "qpid/framing/amqp_types.h" +#include "qpid/sys/Timer.h" #include "qpid/sys/Mutex.h" namespace qpid { @@ -35,7 +35,7 @@ namespace broker { class DtxManager{ typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap; - struct DtxCleanup : public TimerTask + struct DtxCleanup : public sys::TimerTask { DtxManager& mgr; const std::string& xid; @@ -47,14 +47,14 @@ class DtxManager{ WorkMap work; TransactionalStore* store; qpid::sys::Mutex lock; - Timer timer; + qpid::sys::Timer& timer; void remove(const std::string& xid); DtxWorkRecord* getWork(const std::string& xid); DtxWorkRecord* createWork(std::string xid); public: - DtxManager(); + DtxManager(qpid::sys::Timer&); ~DtxManager(); void start(const std::string& xid, DtxBuffer::shared_ptr work); void join(const std::string& xid, DtxBuffer::shared_ptr work); diff --git a/cpp/src/qpid/broker/DtxTimeout.cpp b/cpp/src/qpid/broker/DtxTimeout.cpp index 8e0a7741c4..f5238d0909 100644 --- a/cpp/src/qpid/broker/DtxTimeout.cpp +++ b/cpp/src/qpid/broker/DtxTimeout.cpp @@ -18,8 +18,8 @@ * under the License. * */ -#include "DtxTimeout.h" -#include "DtxManager.h" +#include "qpid/broker/DtxTimeout.h" +#include "qpid/broker/DtxManager.h" #include "qpid/sys/Time.h" using namespace qpid::broker; diff --git a/cpp/src/qpid/broker/DtxTimeout.h b/cpp/src/qpid/broker/DtxTimeout.h index 6e949eab0d..680a210e4f 100644 --- a/cpp/src/qpid/broker/DtxTimeout.h +++ b/cpp/src/qpid/broker/DtxTimeout.h @@ -22,7 +22,7 @@ #define _DtxTimeout_ #include "qpid/Exception.h" -#include "Timer.h" +#include "qpid/sys/Timer.h" namespace qpid { namespace broker { @@ -31,12 +31,12 @@ class DtxManager; struct DtxTimeoutException : public Exception {}; -struct DtxTimeout : public TimerTask +struct DtxTimeout : public sys::TimerTask { const uint32_t timeout; DtxManager& mgr; const std::string xid; - + DtxTimeout(uint32_t timeout, DtxManager& mgr, const std::string& xid); void fire(); }; diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp index cc79813dab..9f33e698db 100644 --- a/cpp/src/qpid/broker/DtxWorkRecord.cpp +++ b/cpp/src/qpid/broker/DtxWorkRecord.cpp @@ -18,7 +18,7 @@ * under the License. * */ -#include "DtxWorkRecord.h" +#include "qpid/broker/DtxWorkRecord.h" #include "qpid/framing/reply_exceptions.h" #include <boost/format.hpp> #include <boost/mem_fn.hpp> @@ -34,7 +34,7 @@ DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const DtxWorkRecord::~DtxWorkRecord() { if (timeout.get()) { - timeout->cancelled = true; + timeout->cancel(); } } diff --git a/cpp/src/qpid/broker/DtxWorkRecord.h b/cpp/src/qpid/broker/DtxWorkRecord.h index 6677784c32..aec2d2aed4 100644 --- a/cpp/src/qpid/broker/DtxWorkRecord.h +++ b/cpp/src/qpid/broker/DtxWorkRecord.h @@ -21,9 +21,10 @@ #ifndef _DtxWorkRecord_ #define _DtxWorkRecord_ -#include "DtxBuffer.h" -#include "DtxTimeout.h" -#include "TransactionalStore.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/DtxBuffer.h" +#include "qpid/broker/DtxTimeout.h" +#include "qpid/broker/TransactionalStore.h" #include "qpid/framing/amqp_types.h" #include "qpid/sys/Mutex.h" @@ -61,12 +62,13 @@ class DtxWorkRecord void abort(); bool prepare(TransactionContext* txn); public: - DtxWorkRecord(const std::string& xid, TransactionalStore* const store); - ~DtxWorkRecord(); - bool prepare(); - bool commit(bool onePhase); - void rollback(); - void add(DtxBuffer::shared_ptr ops); + QPID_BROKER_EXTERN DtxWorkRecord(const std::string& xid, + TransactionalStore* const store); + QPID_BROKER_EXTERN ~DtxWorkRecord(); + QPID_BROKER_EXTERN bool prepare(); + QPID_BROKER_EXTERN bool commit(bool onePhase); + QPID_BROKER_EXTERN void rollback(); + QPID_BROKER_EXTERN void add(DtxBuffer::shared_ptr ops); void recover(std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops); void timedout(); void setTimeout(boost::intrusive_ptr<DtxTimeout> t) { timeout = t; } diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp index fbfcaede82..8efb9ac545 100644 --- a/cpp/src/qpid/broker/Exchange.cpp +++ b/cpp/src/qpid/broker/Exchange.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 @@ -19,52 +19,157 @@ * */ -#include "Exchange.h" -#include "ExchangeRegistry.h" -#include "qpid/agent/ManagementAgent.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/broker/DeliverableMessage.h" using namespace qpid::broker; +using namespace qpid::framing; using qpid::framing::Buffer; using qpid::framing::FieldTable; +using qpid::sys::Mutex; using qpid::management::ManagementAgent; using qpid::management::ManagementObject; using qpid::management::Manageable; using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; -Exchange::Exchange (const string& _name, Manageable* parent) : - name(_name), durable(false), persistenceId(0), mgmtExchange(0) +namespace { - if (parent != 0) +const std::string qpidMsgSequence("qpid.msg_sequence"); +const std::string qpidSequenceCounter("qpid.sequence_counter"); +const std::string qpidIVE("qpid.ive"); +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); + +const std::string QPID_MANAGEMENT("qpid.management"); +} + + +Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) { + if (parent){ + if (parent->sequence || parent->ive) parent->sequenceLock.lock(); + + if (parent->sequence){ + parent->sequenceNo++; + msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo); + } + if (parent->ive) { + parent->lastMsg = &( msg.getMessage()); + } + } +} + +Exchange::PreRoute::~PreRoute(){ + if (parent && (parent->sequence || parent->ive)){ + parent->sequenceLock.unlock(); + } +} + +void Exchange::doRoute(Deliverable& msg, ConstBindingList b) +{ + int count = 0; + + if (b.get()) { + // Block the content release if the message is transient AND there is more than one binding + if (!msg.getMessage().isPersistent() && b->size() > 1) + msg.getMessage().blockContentRelease(); + + for(std::vector<Binding::shared_ptr>::const_iterator i = b->begin(); i != b->end(); i++, count++) { + msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding != 0) + (*i)->mgmtBinding->inc_msgMatched(); + } + } + + if (mgmtExchange != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.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 ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + +void Exchange::routeIVE(){ + if (ive && lastMsg.get()){ + DeliverableMessage dmsg(lastMsg); + route(dmsg, lastMsg->getRoutingKey(), lastMsg->getApplicationHeaders()); + } +} + + +Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) : + name(_name), durable(false), persistenceId(0), sequence(false), + sequenceNo(0), ive(false), mgmtExchange(0), broker(b) +{ + if (parent != 0 && broker != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtExchange = new management::Exchange (agent, this, parent, _name, durable); + mgmtExchange = new _qmf::Exchange (agent, this, parent, _name); + mgmtExchange->set_durable(durable); + mgmtExchange->set_autoDelete(false); agent->addObject (mgmtExchange); } } } Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, - Manageable* parent) - : name(_name), durable(_durable), args(_args), alternateUsers(0), persistenceId(0), mgmtExchange(0) + Manageable* parent, Broker* b) + : name(_name), durable(_durable), alternateUsers(0), persistenceId(0), + args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b) { - if (parent != 0) + if (parent != 0 && broker != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtExchange = new management::Exchange (agent, this, parent, _name, durable); + mgmtExchange = new _qmf::Exchange (agent, this, parent, _name); + mgmtExchange->set_durable(durable); + mgmtExchange->set_autoDelete(false); + mgmtExchange->set_arguments(args); if (!durable) { - if (name == "") - agent->addObject (mgmtExchange, 4, 1); // Special default exchange ID - else if (name == "qpid.management") - agent->addObject (mgmtExchange, 5, 1); // Special management exchange ID - else - agent->addObject (mgmtExchange); + if (name.empty()) { + agent->addObject (mgmtExchange, 0x1000000000000004LL); // Special default exchange ID + } else if (name == QPID_MANAGEMENT) { + agent->addObject (mgmtExchange, 0x1000000000000005LL); // Special management exchange ID + } else { + agent->addObject (mgmtExchange, agent->allocateId(this)); + } } } } + + sequence = _args.get(qpidMsgSequence); + if (sequence) { + QPID_LOG(debug, "Configured exchange " << _name << " with Msg sequencing"); + args.setInt64(std::string(qpidSequenceCounter), sequenceNo); + } + + ive = _args.get(qpidIVE); + if (ive) QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value"); } Exchange::~Exchange () @@ -73,12 +178,23 @@ Exchange::~Exchange () mgmtExchange->resourceDestroy (); } +void Exchange::setAlternate(Exchange::shared_ptr _alternate) +{ + alternate = _alternate; + if (mgmtExchange != 0) { + if (alternate.get() != 0) + mgmtExchange->set_altExchange(alternate->GetManagementObject()->getObjectId()); + else + mgmtExchange->clr_altExchange(); + } +} + void Exchange::setPersistenceId(uint64_t id) const { if (mgmtExchange != 0 && persistenceId == 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); - agent->addObject (mgmtExchange, id, 2); + ManagementAgent* agent = broker->getManagementAgent(); + agent->addObject (mgmtExchange, 0x2000000000000000LL + id); } persistenceId = id; } @@ -87,30 +203,58 @@ Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffe { string name; string type; + string altName; FieldTable args; - + buffer.getShortString(name); bool durable(buffer.getOctet()); buffer.getShortString(type); buffer.get(args); + // For backwards compatibility on restoring exchanges from before the alt-exchange update, perform check + if (buffer.available()) + buffer.getShortString(altName); - return exchanges.declare(name, type, durable, args).first; + try { + Exchange::shared_ptr exch = exchanges.declare(name, type, durable, args).first; + exch->sequenceNo = args.getAsInt64(qpidSequenceCounter); + exch->alternateName.assign(altName); + return exch; + } catch (const UnknownExchangeTypeException&) { + QPID_LOG(warning, "Could not create exchange " << name << "; type " << type << " is not recognised"); + return Exchange::shared_ptr(); + } } -void Exchange::encode(Buffer& buffer) const +void Exchange::encode(Buffer& buffer) const { buffer.putShortString(name); buffer.putOctet(durable); buffer.putShortString(getType()); + if (args.isSet(qpidSequenceCounter)) + args.setInt64(std::string(qpidSequenceCounter),sequenceNo); buffer.put(args); + buffer.putShortString(alternate.get() ? alternate->getName() : string("")); } -uint32_t Exchange::encodedSize() const -{ +uint32_t Exchange::encodedSize() const +{ return name.size() + 1/*short string size*/ + 1 /*durable*/ + getType().size() + 1/*short string size*/ - + args.size(); + + (alternate.get() ? alternate->getName().size() : 0) + 1/*short string size*/ + + args.encodedSize(); +} + +void Exchange::recoveryComplete(ExchangeRegistry& exchanges) +{ + if (!alternateName.empty()) { + try { + Exchange::shared_ptr ae = exchanges.get(alternateName); + setAlternate(ae); + } catch (const NotFoundException&) { + QPID_LOG(warning, "Could not set alternate exchange \"" << alternateName << "\": does not exist."); + } + } } ManagementObject* Exchange::GetManagementObject (void) const @@ -118,30 +262,85 @@ ManagementObject* Exchange::GetManagementObject (void) const return (ManagementObject*) mgmtExchange; } +void Exchange::registerDynamicBridge(DynamicBridge* db) +{ + if (!supportsDynamicBinding()) + throw Exception("Exchange type does not support dynamic binding"); + + { + Mutex::ScopedLock l(bridgeLock); + for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin(); + iter != bridgeVector.end(); iter++) + (*iter)->sendReorigin(); + + bridgeVector.push_back(db); + } + + FieldTable args; + args.setString(qpidFedOp, fedOpReorigin); + bind(Queue::shared_ptr(), string(), &args); +} + +void Exchange::removeDynamicBridge(DynamicBridge* db) +{ + Mutex::ScopedLock l(bridgeLock); + for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin(); + iter != bridgeVector.end(); iter++) + if (*iter == db) { + bridgeVector.erase(iter); + break; + } +} + +void Exchange::handleHelloRequest() +{ +} + +void Exchange::propagateFedOp(const string& routingKey, const string& tags, const string& op, const string& origin) +{ + Mutex::ScopedLock l(bridgeLock); + string myOp(op.empty() ? fedOpBind : op); + + for (std::vector<DynamicBridge*>::iterator iter = bridgeVector.begin(); + iter != bridgeVector.end(); iter++) + (*iter)->propagateBinding(routingKey, tags, op, origin); +} + Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* parent, - FieldTable _args) + FieldTable _args, const string& origin) : queue(_queue), key(_key), args(_args), mgmtBinding(0) { if (parent != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); - if (agent != 0) - { - ManagementObject* mo = queue->GetManagementObject(); - if (mo != 0) - { - uint64_t queueId = mo->getObjectId(); - mgmtBinding = new management::Binding (agent, this, (Manageable*) parent, queueId, key, args); - agent->addObject (mgmtBinding); - } + Broker* broker = parent->getBroker(); + if (broker != 0) { + ManagementAgent* agent = broker->getManagementAgent(); + if (agent != 0) + { + ManagementObject* mo = queue->GetManagementObject(); + if (mo != 0) + { + management::ObjectId queueId = mo->getObjectId(); + mgmtBinding = new _qmf::Binding + (agent, this, (Manageable*) parent, queueId, key, args); + if (!origin.empty()) + mgmtBinding->set_origin(origin); + agent->addObject (mgmtBinding, agent->allocateId(this)); + static_cast<_qmf::Queue*>(mo)->inc_bindingCount(); + } + } } } } Exchange::Binding::~Binding () { - if (mgmtBinding != 0) + if (mgmtBinding != 0) { + ManagementObject* mo = queue->GetManagementObject(); + if (mo != 0) + static_cast<_qmf::Queue*>(mo)->dec_bindingCount(); mgmtBinding->resourceDestroy (); + } } ManagementObject* Exchange::Binding::GetManagementObject () const @@ -149,7 +348,13 @@ ManagementObject* Exchange::Binding::GetManagementObject () const return (ManagementObject*) mgmtBinding; } -Manageable::status_t Exchange::Binding::ManagementMethod (uint32_t, Args&) +Exchange::MatchQueue::MatchQueue(Queue::shared_ptr q) : queue(q) {} + +bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b) { - return Manageable::STATUS_UNKNOWN_METHOD; + return b->queue == queue; +} + +void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) { + msg->getProperties<DeliveryProperties>()->setExchange(getName()); } diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h index f4ac4373e4..d630f7ae24 100644 --- a/cpp/src/qpid/broker/Exchange.h +++ b/cpp/src/qpid/broker/Exchange.h @@ -23,87 +23,171 @@ */ #include <boost/shared_ptr.hpp> -#include "Deliverable.h" -#include "Queue.h" -#include "MessageStore.h" -#include "PersistableExchange.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/PersistableExchange.h" #include "qpid/framing/FieldTable.h" +#include "qpid/sys/Mutex.h" #include "qpid/management/Manageable.h" -#include "qpid/management/Exchange.h" -#include "qpid/management/Binding.h" +#include "qmf/org/apache/qpid/broker/Exchange.h" +#include "qmf/org/apache/qpid/broker/Binding.h" namespace qpid { - namespace broker { - using std::string; - class ExchangeRegistry; - - class Exchange : public PersistableExchange, public management::Manageable { - private: - const string name; - const bool durable; - qpid::framing::FieldTable args; - boost::shared_ptr<Exchange> alternate; - uint32_t alternateUsers; - mutable uint64_t persistenceId; - - protected: - struct Binding : public management::Manageable { - typedef boost::shared_ptr<Binding> shared_ptr; - typedef std::vector<Binding::shared_ptr> vector; - - Queue::shared_ptr queue; - const std::string key; - const framing::FieldTable args; - management::Binding* mgmtBinding; - - Binding(const std::string& key, Queue::shared_ptr queue, Exchange* parent = 0, - framing::FieldTable args = framing::FieldTable ()); - ~Binding (); - management::ManagementObject* GetManagementObject () const; - management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args); - }; - - management::Exchange* mgmtExchange; - - public: - typedef boost::shared_ptr<Exchange> shared_ptr; - - explicit Exchange(const string& name, management::Manageable* parent = 0); - Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, - management::Manageable* parent = 0); - virtual ~Exchange(); - - const string& getName() const { return name; } - bool isDurable() { return durable; } - qpid::framing::FieldTable& getArgs() { return args; } - - Exchange::shared_ptr getAlternate() { return alternate; } - void setAlternate(Exchange::shared_ptr _alternate) { alternate = _alternate; } - void incAlternateUsers() { alternateUsers++; } - void decAlternateUsers() { alternateUsers--; } - bool inUseAsAlternate() { return alternateUsers > 0; } - - virtual string getType() const = 0; - virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0; - virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0; - virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args) = 0; - virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args) = 0; - - //PersistableExchange: - void setPersistenceId(uint64_t id) const; - uint64_t getPersistenceId() const { return persistenceId; } - uint32_t encodedSize() const; - void encode(framing::Buffer& buffer) const; - - static Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer); - - // Manageable entry points - management::ManagementObject* GetManagementObject (void) const; - management::Manageable::status_t - ManagementMethod (uint32_t, management::Args&) { return management::Manageable::STATUS_UNKNOWN_METHOD; } - }; - } -} - +namespace broker { + +class ExchangeRegistry; + +class Exchange : public PersistableExchange, public management::Manageable { +public: + struct Binding : public management::Manageable { + typedef boost::shared_ptr<Binding> shared_ptr; + typedef std::vector<Binding::shared_ptr> vector; + + Queue::shared_ptr queue; + const std::string key; + const framing::FieldTable args; + qmf::org::apache::qpid::broker::Binding* mgmtBinding; + + Binding(const std::string& key, Queue::shared_ptr queue, Exchange* parent = 0, + framing::FieldTable args = framing::FieldTable(), const std::string& origin = std::string()); + ~Binding(); + management::ManagementObject* GetManagementObject() const; + }; + +private: + const std::string name; + const bool durable; + std::string alternateName; + boost::shared_ptr<Exchange> alternate; + uint32_t alternateUsers; + mutable uint64_t persistenceId; + +protected: + mutable qpid::framing::FieldTable args; + bool sequence; + mutable qpid::sys::Mutex sequenceLock; + int64_t sequenceNo; + bool ive; + boost::intrusive_ptr<Message> lastMsg; + + class PreRoute{ + public: + PreRoute(Deliverable& msg, Exchange* _p); + ~PreRoute(); + private: + Exchange* parent; + }; + + typedef boost::shared_ptr<const std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> > > ConstBindingList; + typedef boost::shared_ptr< std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> > > BindingList; + void doRoute(Deliverable& msg, ConstBindingList b); + void routeIVE(); + + + struct MatchQueue { + const Queue::shared_ptr queue; + MatchQueue(Queue::shared_ptr q); + bool operator()(Exchange::Binding::shared_ptr b); + }; + + class FedBinding { + uint32_t localBindings; + std::set<std::string> originSet; + public: + FedBinding() : localBindings(0) {} + bool hasLocal() const { return localBindings != 0; } + bool addOrigin(const std::string& origin) { + if (origin.empty()) { + localBindings++; + return localBindings == 1; + } + originSet.insert(origin); + return true; + } + bool delOrigin(const std::string& origin) { + originSet.erase(origin); + return true; + } + bool delOrigin() { + if (localBindings > 0) + localBindings--; + return localBindings == 0; + } + uint32_t count() { + return localBindings + originSet.size(); + } + }; + + qmf::org::apache::qpid::broker::Exchange* mgmtExchange; + +public: + typedef boost::shared_ptr<Exchange> shared_ptr; + + QPID_BROKER_EXTERN explicit Exchange(const std::string& name, management::Manageable* parent = 0, + Broker* broker = 0); + QPID_BROKER_EXTERN Exchange(const std::string& _name, bool _durable, const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0, Broker* broker = 0); + QPID_BROKER_EXTERN virtual ~Exchange(); + + const std::string& getName() const { return name; } + bool isDurable() { return durable; } + qpid::framing::FieldTable& getArgs() { return args; } + + Exchange::shared_ptr getAlternate() { return alternate; } + void setAlternate(Exchange::shared_ptr _alternate); + void incAlternateUsers() { alternateUsers++; } + void decAlternateUsers() { alternateUsers--; } + bool inUseAsAlternate() { return alternateUsers > 0; } + + virtual std::string getType() const = 0; + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual bool isBound(Queue::shared_ptr 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; + + //PersistableExchange: + QPID_BROKER_EXTERN void setPersistenceId(uint64_t id) const; + uint64_t getPersistenceId() const { return persistenceId; } + QPID_BROKER_EXTERN uint32_t encodedSize() const; + QPID_BROKER_EXTERN virtual void encode(framing::Buffer& buffer) const; + + static QPID_BROKER_EXTERN Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer); + + // Manageable entry points + QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject(void) const; + + // Federation hooks + class DynamicBridge { + public: + virtual ~DynamicBridge() {} + virtual void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin) = 0; + virtual void sendReorigin() = 0; + virtual bool containsLocalTag(const std::string& tagList) const = 0; + virtual const std::string& getLocalTag() const = 0; + }; + + void registerDynamicBridge(DynamicBridge* db); + void removeDynamicBridge(DynamicBridge* db); + virtual bool supportsDynamicBinding() { return false; } + Broker* getBroker() const { return broker; } + /** + * Notify exchange that recovery has completed. + */ + void recoveryComplete(ExchangeRegistry& exchanges); + +protected: + qpid::sys::Mutex bridgeLock; + std::vector<DynamicBridge*> bridgeVector; + Broker* broker; + + QPID_BROKER_EXTERN virtual void handleHelloRequest(); + void propagateFedOp(const std::string& routingKey, const std::string& tags, + const std::string& op, const std::string& origin); +}; + +}} #endif /*!_broker_Exchange.cpp_h*/ diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp index 45eb308680..951cdbd395 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.cpp +++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -19,15 +19,11 @@ * */ -#include "config.h" -#include "ExchangeRegistry.h" -#include "DirectExchange.h" -#include "FanOutExchange.h" -#include "HeadersExchange.h" -#include "TopicExchange.h" -#ifdef HAVE_XML -#include "XmlExchange.h" -#endif +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/broker/TopicExchange.h" #include "qpid/management/ManagementExchange.h" #include "qpid/framing/reply_exceptions.h" @@ -36,42 +32,35 @@ using namespace qpid::sys; using std::pair; using qpid::framing::FieldTable; -pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type) - throw(UnknownExchangeTypeException){ +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type){ return declare(name, type, false, FieldTable()); } pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type, - bool durable, const FieldTable& args) - throw(UnknownExchangeTypeException){ + bool durable, const FieldTable& args){ RWlock::ScopedWlock locker(lock); ExchangeMap::iterator i = exchanges.find(name); if (i == exchanges.end()) { Exchange::shared_ptr exchange; if(type == TopicExchange::typeName){ - exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent)); + exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent, broker)); }else if(type == DirectExchange::typeName){ - exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent)); + exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent, broker)); }else if(type == FanOutExchange::typeName){ - exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent)); + exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent, broker)); }else if (type == HeadersExchange::typeName) { - exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent)); + exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent, broker)); }else if (type == ManagementExchange::typeName) { - exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent)); + exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent, broker)); } -#ifdef HAVE_XML - else if (type == XmlExchange::typeName) { - exchange = Exchange::shared_ptr(new XmlExchange(name, durable, args, parent)); - } -#endif else{ FunctionMap::iterator i = factory.find(type); if (i == factory.end()) { throw UnknownExchangeTypeException(); } else { - exchange = i->second(name, durable, args, parent); + exchange = i->second(name, durable, args, parent, broker); } } exchanges[name] = exchange; @@ -82,6 +71,11 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c } void ExchangeRegistry::destroy(const string& name){ + if (name.empty() || + (name.find("amq.") == 0 && + (name == "amq.direct" || name == "amq.fanout" || name == "amq.topic" || name == "amq.match")) || + name == "qpid.management") + throw framing::NotAllowedException(QPID_MSG("Cannot delete default exchange: '" << name << "'")); RWlock::ScopedWlock locker(lock); ExchangeMap::iterator i = exchanges.find(name); if (i != exchanges.end()) { @@ -97,6 +91,10 @@ Exchange::shared_ptr ExchangeRegistry::get(const string& name){ return i->second; } +bool ExchangeRegistry::registerExchange(const Exchange::shared_ptr& ex) { + return exchanges.insert(ExchangeMap::value_type(ex->getName(), ex)).second; +} + void ExchangeRegistry::registerType(const std::string& type, FactoryFunction f) { factory[type] = f; diff --git a/cpp/src/qpid/broker/ExchangeRegistry.h b/cpp/src/qpid/broker/ExchangeRegistry.h index 7573e3e415..2b75a8f3cf 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.h +++ b/cpp/src/qpid/broker/ExchangeRegistry.h @@ -22,51 +22,72 @@ * */ -#include <map> -#include <boost/function.hpp> -#include "Exchange.h" -#include "MessageStore.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/MessageStore.h" #include "qpid/framing/FieldTable.h" #include "qpid/sys/Monitor.h" #include "qpid/management/Manageable.h" +#include <boost/function.hpp> +#include <boost/bind.hpp> + +#include <algorithm> +#include <map> + namespace qpid { namespace broker { - struct UnknownExchangeTypeException{}; - - class ExchangeRegistry{ - public: - typedef boost::function4<Exchange::shared_ptr, const std::string&, - bool, const qpid::framing::FieldTable&, qpid::management::Manageable*> FactoryFunction; - - ExchangeRegistry () : parent(0) {} - std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type) - throw(UnknownExchangeTypeException); - std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type, - bool durable, const qpid::framing::FieldTable& args = framing::FieldTable()) - throw(UnknownExchangeTypeException); - void destroy(const std::string& name); - Exchange::shared_ptr get(const std::string& name); - Exchange::shared_ptr getDefault(); - - /** - * Register the manageable parent for declared exchanges - */ - void setParent (management::Manageable* _parent) { parent = _parent; } - - void registerType(const std::string& type, FactoryFunction); - private: - typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap; - typedef std::map<std::string, FactoryFunction > FunctionMap; - - ExchangeMap exchanges; - FunctionMap factory; - qpid::sys::RWlock lock; - management::Manageable* parent; - - }; -} -} + +struct UnknownExchangeTypeException{}; + +class ExchangeRegistry{ + public: + typedef boost::function5<Exchange::shared_ptr, const std::string&, + bool, const qpid::framing::FieldTable&, qpid::management::Manageable*, qpid::broker::Broker*> FactoryFunction; + + ExchangeRegistry (Broker* b = 0) : parent(0), broker(b) {} + QPID_BROKER_EXTERN std::pair<Exchange::shared_ptr, bool> declare + (const std::string& name, const std::string& type); + QPID_BROKER_EXTERN std::pair<Exchange::shared_ptr, bool> declare + (const std::string& name, + const std::string& type, + bool durable, + const qpid::framing::FieldTable& args = framing::FieldTable()); + QPID_BROKER_EXTERN void destroy(const std::string& name); + QPID_BROKER_EXTERN Exchange::shared_ptr get(const std::string& name); + Exchange::shared_ptr getDefault(); + + /** + * Register the manageable parent for declared exchanges + */ + void setParent (management::Manageable* _parent) { parent = _parent; } + + /** 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 void registerType(const std::string& type, FactoryFunction); + + /** Call f for each exchange in the registry. */ + template <class F> void eachExchange(F f) const { + qpid::sys::RWlock::ScopedRlock l(lock); + 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; + + ExchangeMap exchanges; + FunctionMap factory; + mutable qpid::sys::RWlock lock; + management::Manageable* parent; + Broker* broker; +}; + +}} // namespace qpid::broker #endif /*!_broker_ExchangeRegistry_h*/ diff --git a/cpp/src/qpid/broker/Prefetch.h b/cpp/src/qpid/broker/ExpiryPolicy.cpp index 8eb27a3e21..64a12d918a 100644 --- a/cpp/src/qpid/broker/Prefetch.h +++ b/cpp/src/qpid/broker/ExpiryPolicy.cpp @@ -18,25 +18,21 @@ * under the License. * */ -#ifndef _Prefetch_ -#define _Prefetch_ - -#include "qpid/framing/amqp_types.h" +#include "qpid/broker/ExpiryPolicy.h" +#include "qpid/broker/Message.h" +#include "qpid/sys/Time.h" namespace qpid { - namespace broker { - /** - * Count and total size of asynchronously delivered - * (i.e. pushed) messages that have acks outstanding. - */ - struct Prefetch{ - uint32_t size; - uint16_t count; +namespace broker { + +ExpiryPolicy::~ExpiryPolicy() {} + +void ExpiryPolicy::willExpire(Message&) {} - void reset() { size = 0; count = 0; } - }; - } +bool ExpiryPolicy::hasExpired(Message& m) { + return m.getExpiration() < sys::AbsTime::now(); } +void ExpiryPolicy::forget(Message&) {} -#endif +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/ExpiryPolicy.h b/cpp/src/qpid/broker/ExpiryPolicy.h new file mode 100644 index 0000000000..40e793bf2c --- /dev/null +++ b/cpp/src/qpid/broker/ExpiryPolicy.h @@ -0,0 +1,46 @@ +#ifndef QPID_BROKER_EXPIRYPOLICY_H +#define QPID_BROKER_EXPIRYPOLICY_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/RefCounted.h" +#include "qpid/broker/BrokerImportExport.h" + +namespace qpid { +namespace broker { + +class Message; + +/** + * Default expiry policy. + */ +class ExpiryPolicy : public RefCounted +{ + public: + QPID_BROKER_EXTERN virtual ~ExpiryPolicy(); + QPID_BROKER_EXTERN virtual void willExpire(Message&); + QPID_BROKER_EXTERN virtual bool hasExpired(Message&); + QPID_BROKER_EXTERN virtual void forget(Message&); +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_EXPIRYPOLICY_H*/ diff --git a/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp index 373e9ab1cc..6d840b50df 100644 --- a/cpp/src/qpid/broker/FanOutExchange.cpp +++ b/cpp/src/qpid/broker/FanOutExchange.cpp @@ -18,106 +18,102 @@ * under the License. * */ -#include "FanOutExchange.h" +#include "qpid/broker/FanOutExchange.h" #include <algorithm> using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; +namespace _qmf = qmf::org::apache::qpid::broker; -FanOutExchange::FanOutExchange(const std::string& _name, Manageable* _parent) : - Exchange(_name, _parent) +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); + +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); +} + +FanOutExchange::FanOutExchange(const std::string& _name, Manageable* _parent, Broker* b) : + Exchange(_name, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); } FanOutExchange::FanOutExchange(const std::string& _name, bool _durable, - const FieldTable& _args, Manageable* _parent) : - Exchange(_name, _durable, _args, _parent) + const FieldTable& _args, Manageable* _parent, Broker* b) : + Exchange(_name, _durable, _args, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); } -bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* /*args*/){ - RWlock::ScopedWlock locker(lock); - std::vector<Binding::shared_ptr>::iterator i; - - // Add if not already present. - for (i = bindings.begin (); i != bindings.end(); i++) - if ((*i)->queue == queue) - break; - - if (i == bindings.end()) { - Binding::shared_ptr binding (new Binding ("", queue, this)); - bindings.push_back(binding); - if (mgmtExchange != 0) { - mgmtExchange->inc_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->inc_bindingCount(); +bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* args) +{ + string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind); + string fedTags(args ? args->getAsString(qpidFedTags) : ""); + string fedOrigin(args ? args->getAsString(qpidFedOrigin) : ""); + bool propagate = false; + + if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { + Binding::shared_ptr binding (new Binding ("", queue, this, FieldTable(), fedOrigin)); + if (bindings.add_unless(binding, MatchQueue(queue))) { + propagate = fedBinding.addOrigin(fedOrigin); + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + } + } else { + return false; + } + } else if (fedOp == fedOpUnbind) { + propagate = fedBinding.delOrigin(fedOrigin); + if (fedBinding.count() == 0) + unbind(queue, "", 0); + } else if (fedOp == fedOpReorigin) { + if (fedBinding.hasLocal()) { + propagateFedOp(string(), string(), fedOpBind, string()); } - return true; - } else { - return false; } -} -bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* /*args*/){ - RWlock::ScopedWlock locker(lock); - std::vector<Binding::shared_ptr>::iterator i; + routeIVE(); + if (propagate) + propagateFedOp(string(), fedTags, fedOp, fedOrigin); + return true; +} - for (i = bindings.begin (); i != bindings.end(); i++) - if ((*i)->queue == queue) - break; +bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* /*args*/) +{ + bool propagate = false; - if (i != bindings.end()) { - bindings.erase(i); + if (bindings.remove_if(MatchQueue(queue))) { + propagate = fedBinding.delOrigin(); if (mgmtExchange != 0) { mgmtExchange->dec_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->dec_bindingCount(); } - return true; } else { return false; } -} - -void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/){ - RWlock::ScopedRlock locker(lock); - uint32_t count(0); - - for(std::vector<Binding::shared_ptr>::iterator i = bindings.begin(); i != bindings.end(); ++i, count++){ - msg.deliverTo((*i)->queue); - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched (); - } - if (mgmtExchange != 0) - { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - if (count == 0) - { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - else - { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } + if (propagate) + propagateFedOp(string(), string(), fedOpUnbind, string()); + return true; } +void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/) +{ + PreRoute pr(msg, this); + doRoute(msg, bindings.snapshot()); +} + bool FanOutExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const) { - std::vector<Binding::shared_ptr>::iterator i; - - for (i = bindings.begin (); i != bindings.end(); i++) - if ((*i)->queue == queue) - break; - - return i != bindings.end(); + BindingsArray::ConstPtr ptr = bindings.snapshot(); + return ptr && std::find_if(ptr->begin(), ptr->end(), MatchQueue(queue)) != ptr->end(); } diff --git a/cpp/src/qpid/broker/FanOutExchange.h b/cpp/src/qpid/broker/FanOutExchange.h index 4bc92f6b28..7bcf6367cf 100644 --- a/cpp/src/qpid/broker/FanOutExchange.h +++ b/cpp/src/qpid/broker/FanOutExchange.h @@ -23,37 +23,47 @@ #include <map> #include <vector> -#include "Exchange.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Exchange.h" #include "qpid/framing/FieldTable.h" -#include "qpid/sys/Monitor.h" -#include "Queue.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "qpid/broker/Queue.h" namespace qpid { namespace broker { class FanOutExchange : public virtual Exchange { - std::vector<Binding::shared_ptr> bindings; - qpid::sys::RWlock lock; - + typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> BindingsArray; + BindingsArray bindings; + FedBinding fedBinding; public: static const std::string typeName; - FanOutExchange(const std::string& name, management::Manageable* parent = 0); - FanOutExchange(const string& _name, bool _durable, - const qpid::framing::FieldTable& _args, - management::Manageable* parent = 0); + QPID_BROKER_EXTERN FanOutExchange(const std::string& name, + management::Manageable* parent = 0, Broker* broker = 0); + QPID_BROKER_EXTERN FanOutExchange(const string& _name, + bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0, Broker* broker = 0); virtual std::string getType() const { return typeName; } - virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue, + const std::string& routingKey, + const qpid::framing::FieldTable* args); 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); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg, + const std::string& routingKey, + const qpid::framing::FieldTable* args); - virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, + const string* const routingKey, + const qpid::framing::FieldTable* const args); - virtual ~FanOutExchange(); + QPID_BROKER_EXTERN virtual ~FanOutExchange(); + virtual bool supportsDynamicBinding() { return true; } }; } diff --git a/cpp/src/qpid/broker/HandlerImpl.h b/cpp/src/qpid/broker/HandlerImpl.h index 4c51e2a826..aae636e818 100644 --- a/cpp/src/qpid/broker/HandlerImpl.h +++ b/cpp/src/qpid/broker/HandlerImpl.h @@ -19,9 +19,9 @@ * */ -#include "SemanticState.h" -#include "SessionContext.h" -#include "ConnectionState.h" +#include "qpid/broker/SemanticState.h" +#include "qpid/broker/SessionContext.h" +#include "qpid/broker/ConnectionState.h" namespace qpid { namespace broker { diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp index 54519a7bf6..38cc0e4050 100644 --- a/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/cpp/src/qpid/broker/HeadersExchange.cpp @@ -18,7 +18,7 @@ * under the License. * */ -#include "HeadersExchange.h" +#include "qpid/broker/HeadersExchange.h" #include "qpid/framing/FieldValue.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" @@ -28,6 +28,7 @@ using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; +namespace _qmf = qmf::org::apache::qpid::broker; // TODO aconway 2006-09-20: More efficient matching algorithm. // The current search algorithm really sucks. @@ -42,16 +43,16 @@ namespace { const std::string empty; } -HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent) : - Exchange(_name, _parent) +HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent, Broker* b) : + Exchange(_name, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); } HeadersExchange::HeadersExchange(const std::string& _name, bool _durable, - const FieldTable& _args, Manageable* _parent) : - Exchange(_name, _durable, _args, _parent) + const FieldTable& _args, Manageable* _parent, Broker* b) : + Exchange(_name, _durable, _args, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); @@ -73,50 +74,26 @@ std::string HeadersExchange::getMatch(const FieldTable* args) } bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){ - RWlock::ScopedWlock locker(lock); std::string what = getMatch(args); if (what != all && what != any) throw InternalErrorException(QPID_MSG("Invalid x-match value binding to headers exchange.")); - Bindings::iterator i; - - for (i = bindings.begin(); i != bindings.end(); i++) - if (i->first == *args && i->second->queue == queue) - break; - - if (i == bindings.end()) { - Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args)); - HeaderMap headerMap(*args, binding); - - bindings.push_back(headerMap); + Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args)); + if (bindings.add_unless(binding, MatchArgs(queue, args))) { if (mgmtExchange != 0) { mgmtExchange->inc_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->inc_bindingCount(); } + routeIVE(); return true; } else { return false; } } -bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){ - RWlock::ScopedWlock locker(lock); - Bindings::iterator i; - for (i = bindings.begin(); i != bindings.end(); i++) { - if (bindingKey.empty() && args) { - if (i->first == *args && i->second->queue == queue) - break; - } else { - if (i->second->key == bindingKey && i->second->queue == queue) - break; - } - } - - if (i != bindings.end()) { - bindings.erase(i); +bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable*){ + if (bindings.remove_if(MatchKey(queue, bindingKey))) { if (mgmtExchange != 0) { mgmtExchange->dec_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->dec_bindingCount(); } return true; } else { @@ -125,41 +102,43 @@ bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, } -void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args){ - if (!args) return;//can't match if there were no headers passed in - - RWlock::ScopedRlock locker(lock); - uint32_t count(0); - - for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i, count++) { - if (match(i->first, *args)) msg.deliverTo(i->second->queue); - if (i->second->mgmtBinding != 0) - i->second->mgmtBinding->inc_msgMatched (); +void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args) +{ + if (!args) { + //can't match if there were no headers passed in + if (mgmtExchange != 0) { + mgmtExchange->inc_msgReceives(); + mgmtExchange->inc_byteReceives(msg.contentSize()); + mgmtExchange->inc_msgDrops(); + mgmtExchange->inc_byteDrops(msg.contentSize()); + } + return; } - if (mgmtExchange != 0) + PreRoute pr(msg, this); + + ConstBindingList p = bindings.snapshot(); + BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >); + if (p.get()) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - if (count == 0) - { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - else - { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + for (std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i) { + if (match((*i)->args, *args)) { + b->push_back(*i); + } } } + doRoute(msg, b); } bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const args) { - for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i) { - if ( (!args || equal(i->first, *args)) && (!queue || i->second->queue == queue)) { - return true; + Bindings::ConstPtr p = bindings.snapshot(); + if (p.get()){ + for (std::vector<Binding::shared_ptr>::const_iterator i = p->begin(); i != p->end(); ++i) { + if ( (!args || equal((*i)->args, *args)) && (!queue || (*i)->queue == queue)) { + return true; + } } } return false; @@ -227,5 +206,15 @@ bool HeadersExchange::equal(const FieldTable& a, const FieldTable& b) { return true; } +HeadersExchange::MatchArgs::MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a) : queue(q), args(a) {} +bool HeadersExchange::MatchArgs::operator()(Exchange::Binding::shared_ptr b) +{ + return b->queue == queue && b->args == *args; +} +HeadersExchange::MatchKey::MatchKey(Queue::shared_ptr q, const std::string& k) : queue(q), key(k) {} +bool HeadersExchange::MatchKey::operator()(Exchange::Binding::shared_ptr b) +{ + return b->queue == queue && b->key == key; +} diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h index 6e101e193a..6425b44251 100644 --- a/cpp/src/qpid/broker/HeadersExchange.h +++ b/cpp/src/qpid/broker/HeadersExchange.h @@ -22,10 +22,12 @@ #define _HeadersExchange_ #include <vector> -#include "Exchange.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Exchange.h" #include "qpid/framing/FieldTable.h" -#include "qpid/sys/Monitor.h" -#include "Queue.h" +#include "qpid/sys/CopyOnWriteArray.h" +#include "qpid/sys/Mutex.h" +#include "qpid/broker/Queue.h" namespace qpid { namespace broker { @@ -33,34 +35,57 @@ namespace broker { class HeadersExchange : public virtual Exchange { typedef std::pair<qpid::framing::FieldTable, Binding::shared_ptr> HeaderMap; - typedef std::vector<HeaderMap> Bindings; + typedef qpid::sys::CopyOnWriteArray<Binding::shared_ptr> Bindings; + + struct MatchArgs + { + const Queue::shared_ptr queue; + const qpid::framing::FieldTable* args; + MatchArgs(Queue::shared_ptr q, const qpid::framing::FieldTable* a); + bool operator()(Exchange::Binding::shared_ptr b); + }; + struct MatchKey + { + const Queue::shared_ptr queue; + const std::string& key; + MatchKey(Queue::shared_ptr q, const std::string& k); + bool operator()(Exchange::Binding::shared_ptr b); + }; Bindings bindings; - qpid::sys::RWlock lock; + qpid::sys::Mutex lock; static std::string getMatch(const framing::FieldTable* args); public: static const std::string typeName; - HeadersExchange(const string& name, management::Manageable* parent = 0); - HeadersExchange(const string& _name, bool _durable, - const qpid::framing::FieldTable& _args, - management::Manageable* parent = 0); + QPID_BROKER_EXTERN HeadersExchange(const string& name, + management::Manageable* parent = 0, Broker* broker = 0); + QPID_BROKER_EXTERN HeadersExchange(const string& _name, + bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0, Broker* broker = 0); virtual std::string getType() const { return typeName; } - virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue, + const string& routingKey, + const qpid::framing::FieldTable* args); virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); - virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg, + const string& routingKey, + const qpid::framing::FieldTable* args); - virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, + const string* const routingKey, + const qpid::framing::FieldTable* const args); - virtual ~HeadersExchange(); + QPID_BROKER_EXTERN virtual ~HeadersExchange(); - static bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); + static QPID_BROKER_EXTERN bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); static bool equal(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); }; diff --git a/cpp/src/qpid/broker/IncomingExecutionContext.cpp b/cpp/src/qpid/broker/IncomingExecutionContext.cpp deleted file mode 100644 index 6c6cae6740..0000000000 --- a/cpp/src/qpid/broker/IncomingExecutionContext.cpp +++ /dev/null @@ -1,143 +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 "IncomingExecutionContext.h" -#include "qpid/Exception.h" - -namespace qpid { -namespace broker { - -using boost::intrusive_ptr; -using qpid::framing::AccumulatedAck; -using qpid::framing::SequenceNumber; -using qpid::framing::SequenceNumberSet; - -void IncomingExecutionContext::noop() -{ - complete(next()); -} - -void IncomingExecutionContext::flush() -{ - for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ) { - if ((*i)->isEnqueueComplete()) { - complete((*i)->getCommandId()); - i = incomplete.erase(i); - } else { - i++; - } - } - window.lwm = completed.mark; -} - -void IncomingExecutionContext::sync() -{ - while (completed.mark < window.hwm) { - wait(); - } -} - -void IncomingExecutionContext::sync(const SequenceNumber& point) -{ - while (!isComplete(point)) { - wait(); - } -} - -/** - * Every call to next() should be followed be either a call to - * complete() - in the case of commands, which are always synchronous - * - or track() - in the case of messages which may be asynchronously - * stored. - */ -SequenceNumber IncomingExecutionContext::next() -{ - return ++window.hwm; -} - -void IncomingExecutionContext::complete(const SequenceNumber& command) -{ - completed.update(command, command); -} - -void IncomingExecutionContext::track(intrusive_ptr<Message> msg) -{ - if (msg->isEnqueueComplete()) { - complete(msg->getCommandId()); - } else { - incomplete.push_back(msg); - } -} - -bool IncomingExecutionContext::isComplete(const SequenceNumber& command) -{ - if (command > window.hwm) { - throw Exception(QPID_MSG("Bad sync request: point exceeds last command received [" - << command.getValue() << " > " << window.hwm.getValue() << "]")); - } - - return completed.covers(command); -} - - -const SequenceNumber& IncomingExecutionContext::getMark() -{ - return completed.mark; -} - -SequenceNumberSet IncomingExecutionContext::getRange() -{ - SequenceNumberSet range; - completed.collectRanges(range); - return range; -} - -void IncomingExecutionContext::wait() -{ - check(); - // for IO flush on the store - for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); i++) { - (*i)->flush(); - } - incomplete.front()->waitForEnqueueComplete(); - flush(); -} - -/** - * This is a check of internal state consistency. - */ -void IncomingExecutionContext::check() -{ - if (incomplete.empty()) { - if (window.hwm != completed.mark) { - //can only happen if there is a call to next() without a - //corresponding call to completed() or track() - or if - //there is a logical error in flush() or - //AccumulatedAck::update() - throw Exception(QPID_MSG("Completion tracking error: window.hwm=" - << window.hwm.getValue() << ", completed.mark=" - << completed.mark.getValue())); - } - } -} - -}} - diff --git a/cpp/src/qpid/broker/IncompleteMessageList.cpp b/cpp/src/qpid/broker/IncompleteMessageList.cpp index dd7bbfc067..02265ab85c 100644 --- a/cpp/src/qpid/broker/IncompleteMessageList.cpp +++ b/cpp/src/qpid/broker/IncompleteMessageList.cpp @@ -18,34 +18,67 @@ * under the License. * */ -#include "IncompleteMessageList.h" -#include "Message.h" +#include "qpid/broker/IncompleteMessageList.h" namespace qpid { namespace broker { +IncompleteMessageList::IncompleteMessageList() : + callback(boost::bind(&IncompleteMessageList::enqueueComplete, this, _1)) +{} + +IncompleteMessageList::~IncompleteMessageList() +{ + sys::Mutex::ScopedLock l(lock); + for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ++i) { + (*i)->resetEnqueueCompleteCallback(); + (*i)->resetDequeueCompleteCallback(); + } +} + void IncompleteMessageList::add(boost::intrusive_ptr<Message> msg) { + sys::Mutex::ScopedLock l(lock); + msg->setEnqueueCompleteCallback(callback); incomplete.push_back(msg); } -void IncompleteMessageList::process(CompletionListener l, bool sync) +void IncompleteMessageList::enqueueComplete(const boost::intrusive_ptr<Message>& ) { + sys::Mutex::ScopedLock l(lock); + lock.notify(); +} + +void IncompleteMessageList::process(const CompletionListener& listen, bool sync) { + sys::Mutex::ScopedLock l(lock); while (!incomplete.empty()) { boost::intrusive_ptr<Message>& msg = incomplete.front(); if (!msg->isEnqueueComplete()) { if (sync){ - msg->flush(); - msg->waitForEnqueueComplete(); + { + sys::Mutex::ScopedUnlock u(lock); + msg->flush(); // Can re-enter IncompleteMessageList::enqueueComplete + } + while (!msg->isEnqueueComplete()) + lock.wait(); } else { //leave the message as incomplete for now return; } } - l(msg); + listen(msg); incomplete.pop_front(); } } +void IncompleteMessageList::each(const CompletionListener& listen) { + Messages snapshot; + { + sys::Mutex::ScopedLock l(lock); + snapshot = incomplete; + } + std::for_each(incomplete.begin(), incomplete.end(), listen); // FIXME aconway 2008-11-07: passed by ref or value? +} + }} diff --git a/cpp/src/qpid/broker/IncompleteMessageList.h b/cpp/src/qpid/broker/IncompleteMessageList.h index 2cfd7bfee5..a4debd1233 100644 --- a/cpp/src/qpid/broker/IncompleteMessageList.h +++ b/cpp/src/qpid/broker/IncompleteMessageList.h @@ -21,25 +21,35 @@ #ifndef _IncompleteMessageList_ #define _IncompleteMessageList_ -#include <list> +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/sys/Monitor.h" +#include "qpid/broker/Message.h" #include <boost/intrusive_ptr.hpp> #include <boost/function.hpp> +#include <list> namespace qpid { namespace broker { -class Message; - class IncompleteMessageList { typedef std::list< boost::intrusive_ptr<Message> > Messages; + + void enqueueComplete(const boost::intrusive_ptr<Message>&); + + sys::Monitor lock; Messages incomplete; + Message::MessageCallback callback; public: - typedef boost::function<void(boost::intrusive_ptr<Message>)> CompletionListener; - - void add(boost::intrusive_ptr<Message> msg); - void process(CompletionListener l, bool sync); + typedef Message::MessageCallback CompletionListener; + + QPID_BROKER_EXTERN IncompleteMessageList(); + QPID_BROKER_EXTERN ~IncompleteMessageList(); + + QPID_BROKER_EXTERN void add(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN void process(const CompletionListener& l, bool sync); + void each(const CompletionListener& l); }; diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp index 05b759f695..cdba18ccf9 100644 --- a/cpp/src/qpid/broker/Link.cpp +++ b/cpp/src/qpid/broker/Link.cpp @@ -19,50 +19,61 @@ * */ -#include "Link.h" -#include "LinkRegistry.h" -#include "Broker.h" -#include "Connection.h" -#include "qpid/agent/ManagementAgent.h" -#include "qpid/management/Link.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/LinkRegistry.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Connection.h" +#include "qmf/org/apache/qpid/broker/EventBrokerLinkUp.h" +#include "qmf/org/apache/qpid/broker/EventBrokerLinkDown.h" #include "boost/bind.hpp" #include "qpid/log/Statement.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/broker/AclModule.h" using namespace qpid::broker; using qpid::framing::Buffer; using qpid::framing::FieldTable; +using qpid::framing::NotAllowedException; +using qpid::framing::connection::CLOSE_CODE_CONNECTION_FORCED; using qpid::management::ManagementAgent; using qpid::management::ManagementObject; using qpid::management::Manageable; using qpid::management::Args; using qpid::sys::Mutex; +using std::stringstream; +namespace _qmf = qmf::org::apache::qpid::broker; Link::Link(LinkRegistry* _links, MessageStore* _store, string& _host, uint16_t _port, - bool _useSsl, + string& _transport, bool _durable, string& _authMechanism, string& _username, string& _password, Broker* _broker, - management::Manageable* parent) - : links(_links), store(_store), host(_host), port(_port), useSsl(_useSsl), durable(_durable), + Manageable* parent) + : links(_links), store(_store), host(_host), port(_port), + transport(_transport), + durable(_durable), authMechanism(_authMechanism), username(_username), password(_password), persistenceId(0), mgmtObject(0), broker(_broker), state(0), visitCount(0), currentInterval(1), closing(false), + updateUrls(false), channelCounter(1), - connection(0) + connection(0), + agent(0) { - if (parent != 0) + if (parent != 0 && broker != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + agent = broker->getManagementAgent(); if (agent != 0) { - mgmtObject = new management::Link(agent, this, parent, _host, _port, _useSsl, _durable); + mgmtObject = new _qmf::Link(agent, this, parent, _host, _port, _transport, _durable); if (!durable) agent->addObject(mgmtObject); } @@ -73,7 +84,7 @@ Link::Link(LinkRegistry* _links, Link::~Link () { if (state == STATE_OPERATIONAL && connection != 0) - connection->close(); + connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); if (mgmtObject != 0) mgmtObject->resourceDestroy (); @@ -95,6 +106,7 @@ void Link::setStateLH (int newState) case STATE_OPERATIONAL : mgmtObject->set_state("Operational"); break; case STATE_FAILED : mgmtObject->set_state("Failed"); break; case STATE_CLOSED : mgmtObject->set_state("Closed"); break; + case STATE_PASSIVE : mgmtObject->set_state("Passive"); break; } } @@ -104,8 +116,9 @@ void Link::startConnectionLH () // Set the state before calling connect. It is possible that connect // will fail synchronously and call Link::closed before returning. setStateLH(STATE_CONNECTING); - broker->connect (host, port, useSsl, + broker->connect (host, port, transport, boost::bind (&Link::closed, this, _1, _2)); + QPID_LOG (debug, "Inter-broker link connecting to " << host << ":" << port); } catch(std::exception& e) { setStateLH(STATE_WAITING); if (mgmtObject != 0) @@ -115,27 +128,39 @@ void Link::startConnectionLH () void Link::established () { - Mutex::ScopedLock mutex(lock); + stringstream addr; + addr << host << ":" << port; - QPID_LOG (info, "Inter-broker link established to " << host << ":" << port); - setStateLH(STATE_OPERATIONAL); - currentInterval = 1; - visitCount = 0; - if (closing) - destroy(); + QPID_LOG (info, "Inter-broker link established to " << addr.str()); + agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str())); + { + Mutex::ScopedLock mutex(lock); + setStateLH(STATE_OPERATIONAL); + currentInterval = 1; + visitCount = 0; + if (closing) + destroy(); + } } void Link::closed (int, std::string text) { Mutex::ScopedLock mutex(lock); + QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text); connection = 0; - if (state == STATE_OPERATIONAL) - QPID_LOG (warning, "Inter-broker link disconnected from " << host << ":" << port); + if (state == STATE_OPERATIONAL) { + stringstream addr; + addr << host << ":" << port; + QPID_LOG (warning, "Inter-broker link disconnected from " << addr.str()); + agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); + } - for (Bridges::iterator i = active.begin(); i != active.end(); i++) + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); created.push_back(*i); + } active.clear(); if (state != STATE_FAILED) @@ -149,32 +174,46 @@ void Link::closed (int, std::string text) destroy(); } -void Link::destroy () +void Link::checkClosePermission() { Mutex::ScopedLock mutex(lock); - Bridges toDelete; + + AclModule* acl = getBroker()->getAcl(); + std::string userID = getUsername() + "@" + getBroker()->getOptions().realm; + if (acl && !acl->authorise(userID,acl::ACT_DELETE,acl::OBJ_LINK,"")){ + throw NotAllowedException("ACL denied delete link request"); + } +} - QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management"); - if (connection) - connection->close(403, "closed by management"); - setStateLH(STATE_CLOSED); +void Link::destroy () +{ + Bridges toDelete; + { + Mutex::ScopedLock mutex(lock); - // Move the bridges to be deleted into a local vector so there is no - // corruption of the iterator caused by bridge deletion. - for (Bridges::iterator i = active.begin(); i != active.end(); i++) - toDelete.push_back(*i); - active.clear(); + QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management"); + if (connection) + connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management"); + + setStateLH(STATE_CLOSED); - for (Bridges::iterator i = created.begin(); i != created.end(); i++) - toDelete.push_back(*i); - created.clear(); + // Move the bridges to be deleted into a local vector so there is no + // corruption of the iterator caused by bridge deletion. + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); + toDelete.push_back(*i); + } + active.clear(); - // Now delete all bridges on this link. + for (Bridges::iterator i = created.begin(); i != created.end(); i++) + toDelete.push_back(*i); + created.clear(); + } + // Now delete all bridges on this link (don't hold the lock for this). for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++) (*i)->destroy(); toDelete.clear(); - links->destroy (host, port); } @@ -186,21 +225,27 @@ void Link::add(Bridge::shared_ptr bridge) void Link::cancel(Bridge::shared_ptr bridge) { - Mutex::ScopedLock mutex(lock); - - for (Bridges::iterator i = created.begin(); i != created.end(); i++) { - if ((*i).get() == bridge.get()) { - created.erase(i); - break; + { + Mutex::ScopedLock mutex(lock); + + for (Bridges::iterator i = created.begin(); i != created.end(); i++) { + if ((*i).get() == bridge.get()) { + created.erase(i); + break; + } } - } - for (Bridges::iterator i = active.begin(); i != active.end(); i++) { - if ((*i).get() == bridge.get()) { - bridge->cancel(); - active.erase(i); - break; + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + if ((*i).get() == bridge.get()) { + cancellations.push_back(bridge); + bridge->closed(); + active.erase(i); + break; + } } } + if (!cancellations.empty()) { + connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); + } } void Link::ioThreadProcessing() @@ -209,8 +254,9 @@ void Link::ioThreadProcessing() if (state != STATE_OPERATIONAL) return; + QPID_LOG(debug, "Link::ioThreadProcessing()"); - //process any pending creates + //process any pending creates and/or cancellations if (!created.empty()) { for (Bridges::iterator i = created.begin(); i != created.end(); ++i) { active.push_back(*i); @@ -218,34 +264,77 @@ void Link::ioThreadProcessing() } created.clear(); } + if (!cancellations.empty()) { + for (Bridges::iterator i = cancellations.begin(); i != cancellations.end(); ++i) { + (*i)->cancel(*connection); + } + cancellations.clear(); + } } void Link::setConnection(Connection* c) { Mutex::ScopedLock mutex(lock); connection = c; + updateUrls = true; } void Link::maintenanceVisit () { Mutex::ScopedLock mutex(lock); + if (connection && updateUrls) { + urls.reset(connection->getKnownHosts()); + QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << urls); + updateUrls = false; + } + if (state == STATE_WAITING) { visitCount++; if (visitCount >= currentInterval) { visitCount = 0; - currentInterval *= 2; - if (currentInterval > MAX_INTERVAL) - currentInterval = MAX_INTERVAL; - startConnectionLH(); + //switch host and port to next in url list if possible + if (!tryFailover()) { + currentInterval *= 2; + if (currentInterval > MAX_INTERVAL) + currentInterval = MAX_INTERVAL; + startConnectionLH(); + } } } - else if (state == STATE_OPERATIONAL && !created.empty() && connection != 0) + else if (state == STATE_OPERATIONAL && (!created.empty() || !cancellations.empty()) && connection != 0) connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); } +void Link::reconnect(const qpid::TcpAddress& a) +{ + Mutex::ScopedLock mutex(lock); + host = a.host; + port = a.port; + startConnectionLH(); + if (mgmtObject != 0) { + stringstream errorString; + errorString << "Failed over to " << a; + mgmtObject->set_lastError(errorString.str()); + } +} + +bool Link::tryFailover() +{ + //TODO: urls only work for TCP at present, update when that has changed + TcpAddress next; + if (transport == Broker::TCP_TRANSPORT && urls.next(next) && + (next.host != host || next.port != port)) { + links->changeAddress(TcpAddress(host, port), next); + QPID_LOG(debug, "Link failing over to " << host << ":" << port); + return true; + } else { + return false; + } +} + uint Link::nextChannel() { Mutex::ScopedLock mutex(lock); @@ -265,7 +354,7 @@ void Link::notifyConnectionForced(const string text) void Link::setPersistenceId(uint64_t id) const { if (mgmtObject != 0 && persistenceId == 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = broker->getManagementAgent(); agent->addObject(mgmtObject, id); } persistenceId = id; @@ -280,19 +369,20 @@ Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer) { string host; uint16_t port; + string transport; string authMechanism; string username; string password; buffer.getShortString(host); port = buffer.getShort(); - bool useSsl(buffer.getOctet()); + buffer.getShortString(transport); bool durable(buffer.getOctet()); buffer.getShortString(authMechanism); buffer.getShortString(username); buffer.getShortString(password); - return links.declare(host, port, useSsl, durable, authMechanism, username, password).first; + return links.declare(host, port, transport, durable, authMechanism, username, password).first; } void Link::encode(Buffer& buffer) const @@ -300,7 +390,7 @@ void Link::encode(Buffer& buffer) const buffer.putShortString(string("link")); buffer.putShortString(host); buffer.putShort(port); - buffer.putOctet(useSsl ? 1 : 0); + buffer.putShortString(transport); buffer.putOctet(durable ? 1 : 0); buffer.putShortString(authMechanism); buffer.putShortString(username); @@ -312,7 +402,7 @@ uint32_t Link::encodedSize() const return host.size() + 1 // short-string (host) + 5 // short-string ("link") + 2 // port - + 1 // useSsl + + transport.size() + 1 // short-string(transport) + 1 // durable + authMechanism.size() + 1 + username.size() + 1 @@ -324,27 +414,48 @@ ManagementObject* Link::GetManagementObject (void) const return (ManagementObject*) mgmtObject; } -Manageable::status_t Link::ManagementMethod (uint32_t op, management::Args& args) +Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& text) { switch (op) { - case management::Link::METHOD_CLOSE : - closing = true; - if (state != STATE_CONNECTING) - destroy(); + case _qmf::Link::METHOD_CLOSE : + checkClosePermission(); + if (!closing) { + closing = true; + if (state != STATE_CONNECTING && connection) { + //connection can only be closed on the connections own IO processing thread + connection->requestIOProcessing(boost::bind(&Link::destroy, this)); + } + } return Manageable::STATUS_OK; - case management::Link::METHOD_BRIDGE : - management::ArgsLinkBridge& iargs = (management::ArgsLinkBridge&) args; + case _qmf::Link::METHOD_BRIDGE : + _qmf::ArgsLinkBridge& iargs = (_qmf::ArgsLinkBridge&) args; + QPID_LOG(debug, "Link::bridge() request received"); // Durable bridges are only valid on durable links - if (iargs.i_durable && !durable) - return Manageable::STATUS_INVALID_PARAMETER; + if (iargs.i_durable && !durable) { + text = "Can't create a durable route on a non-durable link"; + return Manageable::STATUS_USER; + } + + if (iargs.i_dynamic) { + Exchange::shared_ptr exchange = getBroker()->getExchanges().get(iargs.i_src); + if (exchange.get() == 0) { + text = "Exchange not found"; + return Manageable::STATUS_USER; + } + if (!exchange->supportsDynamicBinding()) { + text = "Exchange type does not support dynamic routing"; + return Manageable::STATUS_USER; + } + } std::pair<Bridge::shared_ptr, bool> result = links->declare (host, port, 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_srcIsLocal, iargs.i_tag, iargs.i_excludes, + iargs.i_dynamic, iargs.i_sync); if (result.second && iargs.i_durable) store->create(*result.first); @@ -354,3 +465,17 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, management::Args& args return Manageable::STATUS_UNKNOWN_METHOD; } + +void Link::setPassive(bool passive) +{ + Mutex::ScopedLock mutex(lock); + if (passive) { + setStateLH(STATE_PASSIVE); + } else { + if (state == STATE_PASSIVE) { + setStateLH(STATE_WAITING); + } else { + QPID_LOG(warning, "Ignoring attempt to activate non-passive link"); + } + } +} diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h index d425c49800..318eb5bd32 100644 --- a/cpp/src/qpid/broker/Link.h +++ b/cpp/src/qpid/broker/Link.h @@ -23,13 +23,15 @@ */ #include <boost/shared_ptr.hpp> -#include "MessageStore.h" -#include "PersistableConfig.h" -#include "Bridge.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/PersistableConfig.h" +#include "qpid/broker/Bridge.h" +#include "qpid/broker/RetryList.h" #include "qpid/sys/Mutex.h" #include "qpid/framing/FieldTable.h" #include "qpid/management/Manageable.h" -#include "qpid/management/Link.h" +#include "qpid/management/ManagementAgent.h" +#include "qmf/org/apache/qpid/broker/Link.h" #include <boost/ptr_container/ptr_vector.hpp> namespace qpid { @@ -47,30 +49,35 @@ namespace qpid { MessageStore* store; string host; uint16_t port; - bool useSsl; + string transport; bool durable; string authMechanism; string username; string password; mutable uint64_t persistenceId; - management::Link* mgmtObject; + qmf::org::apache::qpid::broker::Link* mgmtObject; Broker* broker; int state; uint32_t visitCount; uint32_t currentInterval; bool closing; + RetryList urls; + bool updateUrls; typedef std::vector<Bridge::shared_ptr> Bridges; Bridges created; // Bridges pending creation Bridges active; // Bridges active + Bridges cancellations; // Bridges pending cancellation uint channelCounter; Connection* connection; + management::ManagementAgent* agent; static const int STATE_WAITING = 1; static const int STATE_CONNECTING = 2; static const int STATE_OPERATIONAL = 3; static const int STATE_FAILED = 4; static const int STATE_CLOSED = 5; + static const int STATE_PASSIVE = 6; static const uint32_t MAX_INTERVAL = 32; @@ -78,6 +85,8 @@ namespace qpid { void startConnectionLH(); // Start the IO Connection void destroy(); // Called when mgmt deletes this link void ioThreadProcessing(); // Called on connection's IO thread by request + bool tryFailover(); // Called during maintenance visit + void checkClosePermission(); // ACL check for explict mgmt call to close this link public: typedef boost::shared_ptr<Link> shared_ptr; @@ -86,7 +95,7 @@ namespace qpid { MessageStore* store, string& host, uint16_t port, - bool useSsl, + string& transport, bool durable, string& authMechanism, string& username, @@ -106,12 +115,15 @@ namespace qpid { void established(); // Called when connection is created void closed(int, std::string); // Called when connection goes away void setConnection(Connection*); // Set pointer to the AMQP Connection + void reconnect(const TcpAddress&); //called by LinkRegistry string getAuthMechanism() { return authMechanism; } string getUsername() { return username; } string getPassword() { return password; } + Broker* getBroker() { return broker; } void notifyConnectionForced(const std::string text); + void setPassive(bool p); // PersistableConfig: void setPersistenceId(uint64_t id) const; @@ -123,8 +135,8 @@ namespace qpid { static Link::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer); // Manageable entry points - management::ManagementObject* GetManagementObject (void) const; - management::Manageable::status_t ManagementMethod (uint32_t, management::Args&); + management::ManagementObject* GetManagementObject(void) const; + management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&); }; } } diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp index 0703c276cf..f32587dd68 100644 --- a/cpp/src/qpid/broker/LinkRegistry.cpp +++ b/cpp/src/qpid/broker/LinkRegistry.cpp @@ -18,20 +18,49 @@ * under the License. * */ -#include "LinkRegistry.h" +#include "qpid/broker/LinkRegistry.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/Connection.h" +#include "qpid/log/Statement.h" #include <iostream> +#include <boost/format.hpp> using namespace qpid::broker; using namespace qpid::sys; using std::pair; using std::stringstream; using boost::intrusive_ptr; +using boost::format; +using boost::str; +namespace _qmf = qmf::org::apache::qpid::broker; #define LINK_MAINT_INTERVAL 2 -LinkRegistry::LinkRegistry (Broker* _broker) : broker(_broker), parent(0), store(0) +// TODO: This constructor is only used by the store unit tests - +// That probably indicates that LinkRegistry isn't correctly +// factored: The persistence element and maintenance element +// should be factored separately +LinkRegistry::LinkRegistry () : + broker(0), timer(0), + parent(0), store(0), passive(false), passiveChanged(false), + realm("") { - timer.add (intrusive_ptr<TimerTask> (new Periodic(*this))); +} + +LinkRegistry::LinkRegistry (Broker* _broker) : + broker(_broker), timer(&broker->getTimer()), + maintenanceTask(new Periodic(*this)), + parent(0), store(0), passive(false), passiveChanged(false), + realm(broker->getOptions().realm) +{ + timer->add(maintenanceTask); +} + +LinkRegistry::~LinkRegistry() +{ + // This test is only necessary if the default constructor above is present + if (maintenanceTask) + maintenanceTask->cancel(); } LinkRegistry::Periodic::Periodic (LinkRegistry& _links) : @@ -40,7 +69,8 @@ LinkRegistry::Periodic::Periodic (LinkRegistry& _links) : void LinkRegistry::Periodic::fire () { links.periodicMaintenance (); - links.timer.add (intrusive_ptr<TimerTask> (new Periodic(links))); + setupNextFire(); + links.timer->add(this); } void LinkRegistry::periodicMaintenance () @@ -49,13 +79,53 @@ void LinkRegistry::periodicMaintenance () linksToDestroy.clear(); bridgesToDestroy.clear(); + if (passiveChanged) { + if (passive) { QPID_LOG(info, "Passivating links"); } + else { QPID_LOG(info, "Activating links"); } + for (LinkMap::iterator i = links.begin(); i != links.end(); i++) { + i->second->setPassive(passive); + } + passiveChanged = false; + } for (LinkMap::iterator i = links.begin(); i != links.end(); i++) i->second->maintenanceVisit(); + //now process any requests for re-addressing + for (AddressMap::iterator i = reMappings.begin(); i != reMappings.end(); i++) + updateAddress(i->first, i->second); + reMappings.clear(); +} + +void LinkRegistry::changeAddress(const qpid::TcpAddress& oldAddress, const qpid::TcpAddress& newAddress) +{ + //done on periodic maintenance thread; hold changes in separate + //map to avoid modifying the link map that is iterated over + reMappings[createKey(oldAddress)] = newAddress; +} + +bool LinkRegistry::updateAddress(const std::string& oldKey, const qpid::TcpAddress& newAddress) +{ + std::string newKey = createKey(newAddress); + if (links.find(newKey) != links.end()) { + QPID_LOG(error, "Attempted to update key from " << oldKey << " to " << newKey << " which is already in use"); + return false; + } else { + LinkMap::iterator i = links.find(oldKey); + if (i == links.end()) { + QPID_LOG(error, "Attempted to update key from " << oldKey << " which does not exist, to " << newKey); + return false; + } else { + links[newKey] = i->second; + i->second->reconnect(newAddress); + links.erase(oldKey); + QPID_LOG(info, "Updated link key from " << oldKey << " to " << newKey); + return true; + } + } } pair<Link::shared_ptr, bool> LinkRegistry::declare(string& host, uint16_t port, - bool useSsl, + string& transport, bool durable, string& authMechanism, string& username, @@ -72,9 +142,10 @@ pair<Link::shared_ptr, bool> LinkRegistry::declare(string& host, { Link::shared_ptr link; - link = Link::shared_ptr (new Link (this, store, host, port, useSsl, durable, + link = Link::shared_ptr (new Link (this, store, host, port, transport, durable, authMechanism, username, password, broker, parent)); + if (passive) link->setPassive(true); links[key] = link; return std::pair<Link::shared_ptr, bool>(link, true); } @@ -90,9 +161,13 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host, bool isQueue, bool isLocal, std::string& tag, - std::string& excludes) + std::string& excludes, + bool dynamic, + uint16_t sync) { Mutex::ScopedLock locker(lock); + QPID_LOG(debug, "Bridge declared " << host << ": " << port << " from " << src << " to " << dest << " (" << key << ")"); + stringstream keystream; keystream << host << ":" << port; string linkKey = string(keystream.str()); @@ -107,7 +182,7 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host, BridgeMap::iterator b = bridges.find(bridgeKey); if (b == bridges.end()) { - management::ArgsLinkBridge args; + _qmf::ArgsLinkBridge args; Bridge::shared_ptr bridge; args.i_durable = durable; @@ -118,6 +193,8 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(std::string& host, args.i_srcIsLocal = isLocal; args.i_tag = tag; args.i_excludes = excludes; + args.i_dynamic = dynamic; + args.i_sync = sync; bridge = Bridge::shared_ptr (new Bridge (l->second.get(), l->second->nextChannel(), @@ -177,7 +254,6 @@ void LinkRegistry::destroy(const std::string& host, void LinkRegistry::setStore (MessageStore* _store) { - assert (store == 0 && _store != 0); store = _store; } @@ -185,66 +261,84 @@ MessageStore* LinkRegistry::getStore() const { return store; } -void LinkRegistry::notifyConnection(const std::string& key, Connection* c) +Link::shared_ptr LinkRegistry::findLink(const std::string& key) { Mutex::ScopedLock locker(lock); LinkMap::iterator l = links.find(key); - if (l != links.end()) - { - l->second->established(); - l->second->setConnection(c); + if (l != links.end()) return l->second; + else return Link::shared_ptr(); +} + +void LinkRegistry::notifyConnection(const std::string& key, Connection* c) +{ + Link::shared_ptr link = findLink(key); + if (link) { + link->established(); + link->setConnection(c); + c->setUserId(str(format("%1%@%2%") % link->getUsername() % realm)); } } void LinkRegistry::notifyClosed(const std::string& key) { - Mutex::ScopedLock locker(lock); - LinkMap::iterator l = links.find(key); - if (l != links.end()) - l->second->closed(0, "Closed by peer"); + Link::shared_ptr link = findLink(key); + if (link) { + link->closed(0, "Closed by peer"); + } } void LinkRegistry::notifyConnectionForced(const std::string& key, const std::string& text) { - Mutex::ScopedLock locker(lock); - LinkMap::iterator l = links.find(key); - if (l != links.end()) - l->second->notifyConnectionForced(text); + Link::shared_ptr link = findLink(key); + if (link) { + link->notifyConnectionForced(text); + } } std::string LinkRegistry::getAuthMechanism(const std::string& key) { - Mutex::ScopedLock locker(lock); - LinkMap::iterator l = links.find(key); - if (l != links.end()) - return l->second->getAuthMechanism(); + Link::shared_ptr link = findLink(key); + if (link) + return link->getAuthMechanism(); return string("ANONYMOUS"); } std::string LinkRegistry::getAuthCredentials(const std::string& key) { - Mutex::ScopedLock locker(lock); - LinkMap::iterator l = links.find(key); - if (l == links.end()) + Link::shared_ptr link = findLink(key); + if (!link) return string(); string result; result += '\0'; - result += l->second->getUsername(); + result += link->getUsername(); result += '\0'; - result += l->second->getPassword(); + result += link->getPassword(); return result; } std::string LinkRegistry::getAuthIdentity(const std::string& key) { - Mutex::ScopedLock locker(lock); - LinkMap::iterator l = links.find(key); - if (l == links.end()) + Link::shared_ptr link = findLink(key); + if (!link) return string(); - return l->second->getUsername(); + return link->getUsername(); } +std::string LinkRegistry::createKey(const qpid::TcpAddress& a) +{ + stringstream keystream; + keystream << a.host << ":" << a.port; + return string(keystream.str()); +} + +void LinkRegistry::setPassive(bool p) +{ + Mutex::ScopedLock locker(lock); + passiveChanged = p != passive; + passive = p; + //will activate or passivate links on maintenance visit +} diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h index 242c0d58ba..09a89298b6 100644 --- a/cpp/src/qpid/broker/LinkRegistry.h +++ b/cpp/src/qpid/broker/LinkRegistry.h @@ -23,23 +23,26 @@ */ #include <map> -#include "Link.h" -#include "Bridge.h" -#include "MessageStore.h" -#include "Timer.h" +#include "qpid/broker/Bridge.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/Address.h" #include "qpid/sys/Mutex.h" +#include "qpid/sys/Timer.h" #include "qpid/management/Manageable.h" +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> namespace qpid { namespace broker { + class Link; class Broker; class Connection; class LinkRegistry { // Declare a timer task to manage the establishment of link connections and the // re-establishment of lost link connections. - struct Periodic : public TimerTask + struct Periodic : public sys::TimerTask { LinkRegistry& links; @@ -48,28 +51,40 @@ namespace broker { void fire(); }; - typedef std::map<std::string, Link::shared_ptr> LinkMap; + typedef std::map<std::string, boost::shared_ptr<Link> > LinkMap; typedef std::map<std::string, Bridge::shared_ptr> BridgeMap; + typedef std::map<std::string, TcpAddress> AddressMap; LinkMap links; LinkMap linksToDestroy; BridgeMap bridges; BridgeMap bridgesToDestroy; + AddressMap reMappings; qpid::sys::Mutex lock; Broker* broker; - Timer timer; + sys::Timer* timer; + boost::intrusive_ptr<qpid::sys::TimerTask> maintenanceTask; management::Manageable* parent; MessageStore* store; + bool passive; + bool passiveChanged; + std::string realm; void periodicMaintenance (); + bool updateAddress(const std::string& oldKey, const TcpAddress& newAddress); + boost::shared_ptr<Link> findLink(const std::string& key); + static std::string createKey(const TcpAddress& address); public: + LinkRegistry (); // Only used in store tests LinkRegistry (Broker* _broker); - std::pair<Link::shared_ptr, bool> + ~LinkRegistry(); + + std::pair<boost::shared_ptr<Link>, bool> declare(std::string& host, uint16_t port, - bool useSsl, + std::string& transport, bool durable, std::string& authMechanism, std::string& username, @@ -84,7 +99,9 @@ namespace broker { bool isQueue, bool isLocal, std::string& id, - std::string& excludes); + std::string& excludes, + bool dynamic, + uint16_t sync); void destroy(const std::string& host, const uint16_t port); void destroy(const std::string& host, @@ -114,6 +131,18 @@ namespace broker { std::string getAuthMechanism (const std::string& key); std::string getAuthCredentials (const std::string& key); std::string getAuthIdentity (const std::string& key); + + /** + * Called by links failing over to new address + */ + void changeAddress(const TcpAddress& oldAddress, const TcpAddress& newAddress); + /** + * Called to alter passive state. In passive state the links + * and bridges managed by a link registry will be recorded and + * updated but links won't actually establish connections and + * bridges won't therefore pull or push any messages. + */ + void setPassive(bool); }; } } diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp index 331bb5e716..47ca7a7ae8 100644 --- a/cpp/src/qpid/broker/Message.cpp +++ b/cpp/src/qpid/broker/Message.cpp @@ -19,8 +19,9 @@ * */ -#include "Message.h" -#include "ExchangeRegistry.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/ExpiryPolicy.h" #include "qpid/StringUtils.h" #include "qpid/framing/frame_functors.h" #include "qpid/framing/FieldTable.h" @@ -30,17 +31,43 @@ #include "qpid/framing/TypeFilter.h" #include "qpid/log/Statement.h" +#include <time.h> + using boost::intrusive_ptr; -using namespace qpid::broker; -using namespace qpid::framing; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::TIME_MSEC; +using qpid::sys::FAR_FUTURE; using std::string; +using namespace qpid::framing; + +namespace qpid { +namespace broker { TransferAdapter Message::TRANSFER; -Message::Message(const SequenceNumber& id) : frames(id), persistenceId(0), redelivered(false), loaded(false), staged(false), publisher(0), adapter(0) {} +Message::Message(const framing::SequenceNumber& id) : + frames(id), persistenceId(0), redelivered(false), loaded(false), + staged(false), forcePersistentPolicy(false), publisher(0), adapter(0), + expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0), requiredCredit(0) {} Message::~Message() { + if (expiryPolicy) + expiryPolicy->forget(*this); +} + +void Message::forcePersistent() +{ + // only set forced bit if we actually need to force. + if (! getAdapter().isPersistent(frames) ){ + forcePersistentPolicy = true; + } +} + +bool Message::isForcedPersistent() +{ + return forcePersistentPolicy; } std::string Message::getRoutingKey() const @@ -71,9 +98,9 @@ const FieldTable* Message::getApplicationHeaders() const return getAdapter().getApplicationHeaders(frames); } -bool Message::isPersistent() +bool Message::isPersistent() const { - return getAdapter().isPersistent(frames); + return (getAdapter().isPersistent(frames) || forcePersistentPolicy); } bool Message::requiresAccept() @@ -81,12 +108,16 @@ bool Message::requiresAccept() return getAdapter().requiresAccept(frames); } -uint32_t Message::getRequiredCredit() const +uint32_t Message::getRequiredCredit() { - //add up payload for all header and content frames in the frameset - SumBodySize sum; - frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>()); - return sum.getSize(); + sys::Mutex::ScopedLock l(lock); + if (!requiredCredit) { + //add up payload for all header and content frames in the frameset + SumBodySize sum; + frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>()); + requiredCredit = sum.getSize(); + } + return requiredCredit; } void Message::encode(framing::Buffer& buffer) const @@ -96,7 +127,7 @@ void Message::encode(framing::Buffer& buffer) const frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>()); //then encode the payload of each content frame - EncodeBody f2(buffer); + framing::EncodeBody f2(buffer); frames.map_if(f2, TypeFilter<CONTENT_BODY>()); } @@ -141,9 +172,9 @@ void Message::decodeContent(framing::Buffer& buffer) if (buffer.available()) { //get the data as a string and set that as the content //body on a frame then add that frame to the frameset - AMQFrame frame; - frame.setBody(AMQContentBody()); + AMQFrame frame((AMQContentBody())); frame.castBody<AMQContentBody>()->decode(buffer, buffer.available()); + frame.setFirstSegment(false); frames.append(frame); } else { //adjust header flags @@ -154,17 +185,31 @@ void Message::decodeContent(framing::Buffer& buffer) loaded = true; } -void Message::releaseContent(MessageStore* _store) +void Message::tryReleaseContent() { - if (!store) { - store = _store; + if (checkContentReleasable()) { + releaseContent(); } +} + +void Message::releaseContent(MessageStore* s) +{ + //deprecated, use setStore(store); releaseContent(); instead + if (!store) setStore(s); + releaseContent(); +} + +void Message::releaseContent() +{ + sys::Mutex::ScopedLock l(lock); if (store) { if (!getPersistenceId()) { intrusive_ptr<PersistableMessage> pmsg(this); store->stage(pmsg); staged = true; } + //ensure required credit is cached before content frames are released + getRequiredCredit(); //remove any content frames from the frameset frames.remove(TypeFilter<CONTENT_BODY>()); setContentReleased(); @@ -182,30 +227,37 @@ void Message::destroy() } } -void Message::sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const +bool Message::getContentFrame(const Queue& queue, AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const +{ + intrusive_ptr<const PersistableMessage> pmsg(this); + + bool done = false; + string& data = frame.castBody<AMQContentBody>()->getData(); + store->loadContent(queue, pmsg, data, offset, maxContentSize); + done = data.size() < maxContentSize; + frame.setBof(false); + frame.setEof(true); + QPID_LOG(debug, "loaded frame" << frame); + if (offset > 0) { + frame.setBos(false); + } + if (!done) { + frame.setEos(false); + } else return false; + return true; +} + +void Message::sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const { - if (isContentReleased()) { - //load content from store in chunks of maxContentSize + sys::Mutex::ScopedLock l(lock); + if (isContentReleased() && !frames.isComplete()) { + sys::Mutex::ScopedUnlock u(lock); uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); - intrusive_ptr<const PersistableMessage> pmsg(this); - - bool done = false; - for (uint64_t offset = 0; !done; offset += maxContentSize) + bool morecontent = true; + for (uint64_t offset = 0; morecontent; offset += maxContentSize) { - AMQFrame frame(in_place<AMQContentBody>()); - string& data = frame.castBody<AMQContentBody>()->getData(); - - store->loadContent(queue, pmsg, data, offset, maxContentSize); - done = data.size() < maxContentSize; - frame.setBof(false); - frame.setEof(true); - if (offset > 0) { - frame.setBos(false); - } - if (!done) { - frame.setEos(false); - } - QPID_LOG(debug, "loaded frame for delivery: " << frame); + AMQFrame frame((AMQContentBody())); + morecontent = getContentFrame(queue, frame, maxContentSize, offset); out.handle(frame); } } else { @@ -253,14 +305,14 @@ bool Message::isContentLoaded() const namespace { - const std::string X_QPID_TRACE("x-qpid.trace"); +const std::string X_QPID_TRACE("x-qpid.trace"); } bool Message::isExcluded(const std::vector<std::string>& excludes) const { const FieldTable* headers = getApplicationHeaders(); if (headers) { - std::string traceStr = headers->getString(X_QPID_TRACE); + std::string traceStr = headers->getAsString(X_QPID_TRACE); if (traceStr.size()) { std::vector<std::string> trace = split(traceStr, ", "); @@ -281,7 +333,7 @@ void Message::addTraceId(const std::string& id) sys::Mutex::ScopedLock l(lock); if (isA<MessageTransferBody>()) { FieldTable& headers = getProperties<MessageProperties>()->getApplicationHeaders(); - std::string trace = headers.getString(X_QPID_TRACE); + std::string trace = headers.getAsString(X_QPID_TRACE); if (trace.empty()) { headers.setString(X_QPID_TRACE, id); } else if (trace.find(id) == std::string::npos) { @@ -291,3 +343,86 @@ void Message::addTraceId(const std::string& id) } } } + +void Message::setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e) +{ + DeliveryProperties* props = getProperties<DeliveryProperties>(); + if (props->getTtl()) { + // AMQP requires setting the expiration property to be posix + // time_t in seconds. TTL is in milliseconds + if (!props->getExpiration()) { + //only set expiration in delivery properties if not already set + time_t now = ::time(0); + props->setExpiration(now + (props->getTtl()/1000)); + } + // Use higher resolution time for the internal expiry calculation. + expiration = AbsTime(AbsTime::now(), Duration(props->getTtl() * TIME_MSEC)); + setExpiryPolicy(e); + } +} + +void Message::setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { + expiryPolicy = e; + if (expiryPolicy) + expiryPolicy->willExpire(*this); +} + +bool Message::hasExpired() +{ + return expiryPolicy && expiryPolicy->hasExpired(*this); +} + +boost::intrusive_ptr<Message>& Message::getReplacementMessage(const Queue* qfor) const +{ + sys::Mutex::ScopedLock l(lock); + Replacement::iterator i = replacement.find(qfor); + if (i != replacement.end()){ + return i->second; + } + return empty; +} + +void Message::setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor) +{ + sys::Mutex::ScopedLock l(lock); + replacement[qfor] = msg; +} + +void Message::allEnqueuesComplete() { + sys::Mutex::ScopedLock l(callbackLock); + MessageCallback* cb = enqueueCallback; + if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); +} + +void Message::allDequeuesComplete() { + sys::Mutex::ScopedLock l(callbackLock); + MessageCallback* cb = dequeueCallback; + if (cb && *cb) (*cb)(intrusive_ptr<Message>(this)); +} + +void Message::setEnqueueCompleteCallback(MessageCallback& cb) { + sys::Mutex::ScopedLock l(callbackLock); + enqueueCallback = &cb; +} + +void Message::resetEnqueueCompleteCallback() { + sys::Mutex::ScopedLock l(callbackLock); + enqueueCallback = 0; +} + +void Message::setDequeueCompleteCallback(MessageCallback& cb) { + sys::Mutex::ScopedLock l(callbackLock); + dequeueCallback = &cb; +} + +void Message::resetDequeueCompleteCallback() { + sys::Mutex::ScopedLock l(callbackLock); + dequeueCallback = 0; +} + +framing::FieldTable& Message::getOrInsertHeaders() +{ + return getProperties<MessageProperties>()->getApplicationHeaders(); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h index 0a95fedea6..375fa9ce26 100644 --- a/cpp/src/qpid/broker/Message.h +++ b/cpp/src/qpid/broker/Message.h @@ -22,33 +22,38 @@ * */ -#include <string> -#include <vector> -#include <boost/shared_ptr.hpp> -#include <boost/variant.hpp> -#include "PersistableMessage.h" -#include "MessageAdapter.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/PersistableMessage.h" +#include "qpid/broker/MessageAdapter.h" #include "qpid/framing/amqp_types.h" #include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include <string> +#include <vector> namespace qpid { - + namespace framing { class FieldTable; class SequenceNumber; } - + namespace broker { class ConnectionToken; class Exchange; class ExchangeRegistry; class MessageStore; class Queue; +class ExpiryPolicy; class Message : public PersistableMessage { public: - Message(const framing::SequenceNumber& id = framing::SequenceNumber()); - ~Message(); + typedef boost::function<void (const boost::intrusive_ptr<Message>&)> MessageCallback; + + QPID_BROKER_EXTERN Message(const framing::SequenceNumber& id = framing::SequenceNumber()); + QPID_BROKER_EXTERN ~Message(); uint64_t getPersistenceId() const { return persistenceId; } void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; } @@ -61,16 +66,22 @@ public: const framing::SequenceNumber& getCommandId() { return frames.getId(); } - uint64_t contentSize() const; + QPID_BROKER_EXTERN uint64_t contentSize() const; - std::string getRoutingKey() const; + QPID_BROKER_EXTERN std::string getRoutingKey() const; const boost::shared_ptr<Exchange> getExchange(ExchangeRegistry&) const; - std::string getExchangeName() const; + QPID_BROKER_EXTERN std::string getExchangeName() const; bool isImmediate() const; - const framing::FieldTable* getApplicationHeaders() const; - bool isPersistent(); + QPID_BROKER_EXTERN const framing::FieldTable* getApplicationHeaders() const; + framing::FieldTable& getOrInsertHeaders(); + QPID_BROKER_EXTERN bool isPersistent() const; bool requiresAccept(); + QPID_BROKER_EXTERN void setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e); + void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e); + bool hasExpired(); + sys::AbsTime getExpiration() const { return expiration; } + framing::FrameSet& getFrames() { return frames; } const framing::FrameSet& getFrames() const { return frames; } @@ -84,15 +95,24 @@ public: return p->get<T>(true); } + template <class T> const T* hasProperties() const { + const qpid::framing::AMQHeaderBody* p = frames.getHeaders(); + return p->get<T>(); + } + template <class T> const T* getMethod() const { return frames.as<T>(); } + template <class T> T* getMethod() { + return frames.as<T>(); + } + template <class T> bool isA() const { return frames.isA<T>(); } - uint32_t getRequiredCredit() const; + uint32_t getRequiredCredit(); void encode(framing::Buffer& buffer) const; void encodeContent(framing::Buffer& buffer) const; @@ -110,26 +130,44 @@ public: uint32_t encodedHeaderSize() const; uint32_t encodedContentSize() const; - void decodeHeader(framing::Buffer& buffer); - void decodeContent(framing::Buffer& buffer); + QPID_BROKER_EXTERN void decodeHeader(framing::Buffer& buffer); + QPID_BROKER_EXTERN void decodeContent(framing::Buffer& buffer); - /** - * Releases the in-memory content data held by this - * message. Must pass in a store from which the data can - * be reloaded. - */ - void releaseContent(MessageStore* store); + void QPID_BROKER_EXTERN tryReleaseContent(); + void releaseContent(); + void releaseContent(MessageStore* s);//deprecated, use 'setStore(store); releaseContent();' instead void destroy(); - void sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const; + bool getContentFrame(const Queue& queue, framing::AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const; + QPID_BROKER_EXTERN void sendContent(const Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const; void sendHeader(framing::FrameHandler& out, uint16_t maxFrameSize) const; - bool isContentLoaded() const; + QPID_BROKER_EXTERN bool isContentLoaded() const; bool isExcluded(const std::vector<std::string>& excludes) const; void addTraceId(const std::string& id); + + void forcePersistent(); + bool isForcedPersistent(); + + boost::intrusive_ptr<Message>& getReplacementMessage(const Queue* qfor) const; + void setReplacementMessage(boost::intrusive_ptr<Message> msg, const Queue* qfor); + + /** Call cb when enqueue is complete, may call immediately. Holds cb by reference. */ + void setEnqueueCompleteCallback(MessageCallback& cb); + void resetEnqueueCompleteCallback(); + + /** Call cb when dequeue is complete, may call immediately. Holds cb by reference. */ + void setDequeueCompleteCallback(MessageCallback& cb); + void resetDequeueCompleteCallback(); private: + typedef std::map<const Queue*,boost::intrusive_ptr<Message> > Replacement; + + MessageAdapter& getAdapter() const; + void allEnqueuesComplete(); + void allDequeuesComplete(); + mutable sys::Mutex lock; framing::FrameSet frames; mutable boost::shared_ptr<Exchange> exchange; @@ -137,12 +175,22 @@ public: bool redelivered; bool loaded; bool staged; + bool forcePersistentPolicy; // used to force message as durable, via a broker policy ConnectionToken* publisher; mutable MessageAdapter* adapter; + qpid::sys::AbsTime expiration; + boost::intrusive_ptr<ExpiryPolicy> expiryPolicy; static TransferAdapter TRANSFER; - MessageAdapter& getAdapter() const; + mutable Replacement replacement; + mutable boost::intrusive_ptr<Message> empty; + + sys::Mutex callbackLock; + MessageCallback* enqueueCallback; + MessageCallback* dequeueCallback; + + uint32_t requiredCredit; }; }} diff --git a/cpp/src/qpid/broker/MessageAdapter.cpp b/cpp/src/qpid/broker/MessageAdapter.cpp index 12f01494de..c0c1c4445a 100644 --- a/cpp/src/qpid/broker/MessageAdapter.cpp +++ b/cpp/src/qpid/broker/MessageAdapter.cpp @@ -19,7 +19,7 @@ * */ -#include "MessageAdapter.h" +#include "qpid/broker/MessageAdapter.h" #include "qpid/framing/DeliveryProperties.h" #include "qpid/framing/MessageProperties.h" diff --git a/cpp/src/qpid/broker/MessageBuilder.cpp b/cpp/src/qpid/broker/MessageBuilder.cpp index eda71ed3da..b1a2b77b05 100644 --- a/cpp/src/qpid/broker/MessageBuilder.cpp +++ b/cpp/src/qpid/broker/MessageBuilder.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,10 +18,11 @@ * under the License. * */ -#include "MessageBuilder.h" +#include "qpid/broker/MessageBuilder.h" -#include "Message.h" -#include "MessageStore.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/NullMessageStore.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/reply_exceptions.h" @@ -29,11 +30,13 @@ using boost::intrusive_ptr; using namespace qpid::broker; using namespace qpid::framing; -namespace +namespace { std::string type_str(uint8_t type); + const std::string QPID_MANAGEMENT("qpid.management"); } -MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : + +MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} void MessageBuilder::handle(AMQFrame& frame) @@ -48,14 +51,13 @@ void MessageBuilder::handle(AMQFrame& frame) if (type == CONTENT_BODY) { //TODO: rethink how to handle non-existent headers(?)... //didn't get a header: add in a dummy - AMQFrame header; - header.setBody(AMQHeaderBody()); + AMQFrame header((AMQHeaderBody())); header.setBof(false); header.setEof(false); - message->getFrames().append(header); + message->getFrames().append(header); } else if (type != HEADER_BODY) { throw CommandInvalidException( - QPID_MSG("Invalid frame sequence for message, expected header or content got " + QPID_MSG("Invalid frame sequence for message, expected header or content got " << type_str(type) << ")")); } state = CONTENT; @@ -72,8 +74,13 @@ void MessageBuilder::handle(AMQFrame& frame) } else { message->getFrames().append(frame); //have we reached the staging limit? if so stage message and release content - if (state == CONTENT && stagingThreshold && message->getFrames().getContentSize() >= stagingThreshold) { - message->releaseContent(store); + if (state == CONTENT + && stagingThreshold + && message->getFrames().getContentSize() >= stagingThreshold + && !NullMessageStore::isNullStore(store) + && message->getExchangeName() != QPID_MANAGEMENT /* don't stage mgnt messages */) + { + message->releaseContent(); staging = true; } } @@ -89,6 +96,7 @@ void MessageBuilder::end() void MessageBuilder::start(const SequenceNumber& id) { message = intrusive_ptr<Message>(new Message(id)); + message->setStore(store); state = METHOD; staging = false; } @@ -101,7 +109,7 @@ const std::string CONTENT_BODY_S = "CONTENT"; const std::string HEARTBEAT_BODY_S = "HEARTBEAT"; const std::string UNKNOWN = "unknown"; -std::string type_str(uint8_t type) +std::string type_str(uint8_t type) { switch(type) { case METHOD_BODY: return METHOD_BODY_S; @@ -117,7 +125,7 @@ std::string type_str(uint8_t type) void MessageBuilder::checkType(uint8_t expected, uint8_t actual) { if (expected != actual) { - throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " << type_str(expected) << " got " << type_str(actual) << ")")); } } diff --git a/cpp/src/qpid/broker/MessageBuilder.h b/cpp/src/qpid/broker/MessageBuilder.h index 395de024ab..e63c108097 100644 --- a/cpp/src/qpid/broker/MessageBuilder.h +++ b/cpp/src/qpid/broker/MessageBuilder.h @@ -21,6 +21,7 @@ #ifndef _MessageBuilder_ #define _MessageBuilder_ +#include "qpid/broker/BrokerImportExport.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/SequenceNumber.h" #include "qpid/RefCounted.h" @@ -34,10 +35,11 @@ namespace qpid { class MessageBuilder : public framing::FrameHandler{ public: - MessageBuilder(MessageStore* const store, uint64_t stagingThreshold); - void handle(framing::AMQFrame& frame); + QPID_BROKER_EXTERN MessageBuilder(MessageStore* const store, + uint64_t stagingThreshold); + QPID_BROKER_EXTERN void handle(framing::AMQFrame& frame); boost::intrusive_ptr<Message> getMessage() { return message; } - void start(const framing::SequenceNumber& id); + QPID_BROKER_EXTERN void start(const framing::SequenceNumber& id); void end(); private: enum State {DORMANT, METHOD, HEADER, CONTENT}; diff --git a/cpp/src/qpid/broker/MessageDelivery.cpp b/cpp/src/qpid/broker/MessageDelivery.cpp deleted file mode 100644 index a757d191e7..0000000000 --- a/cpp/src/qpid/broker/MessageDelivery.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 "MessageDelivery.h" - -#include "DeliveryToken.h" -#include "Message.h" -#include "Queue.h" -#include "qpid/framing/FrameHandler.h" -#include "qpid/framing/MessageTransferBody.h" - - -using namespace boost; -using namespace qpid::broker; -using namespace qpid::framing; - -namespace qpid{ -namespace broker{ - -struct BaseToken : DeliveryToken -{ - virtual ~BaseToken() {} - virtual AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId id) = 0; -}; - -struct MessageDeliveryToken : BaseToken -{ - const std::string destination; - const uint8_t confirmMode; - const uint8_t acquireMode; - const bool isPreview; - - MessageDeliveryToken(const std::string& d, uint8_t c, uint8_t a, bool p) : - destination(d), confirmMode(c), acquireMode(a), isPreview(p) {} - - AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId /*id*/) - { - //may need to set the redelivered flag: - if (msg->getRedelivered()){ - msg->getProperties<DeliveryProperties>()->setRedelivered(true); - } - return AMQFrame(in_place<MessageTransferBody>( - ProtocolVersion(), destination, confirmMode, acquireMode)); - } -}; - -} -} - -DeliveryToken::shared_ptr MessageDelivery::getMessageDeliveryToken(const std::string& destination, - uint8_t confirmMode, uint8_t acquireMode) -{ - return DeliveryToken::shared_ptr(new MessageDeliveryToken(destination, confirmMode, acquireMode, false)); -} - -void MessageDelivery::deliver(QueuedMessage& msg, - framing::FrameHandler& handler, - DeliveryId id, - DeliveryToken::shared_ptr token, - uint16_t framesize) -{ - //currently a message published from one class and delivered to - //another may well have the wrong headers; however we will only - //have one content class for 0-10 proper - - boost::shared_ptr<BaseToken> t = dynamic_pointer_cast<BaseToken>(token); - AMQFrame method = t->sendMethod(msg.payload, id); - method.setEof(false); - handler.handle(method); - msg.payload->sendHeader(handler, framesize); - msg.payload->sendContent(*(msg.queue), handler, framesize); -} diff --git a/cpp/src/qpid/broker/MessageDelivery.h b/cpp/src/qpid/broker/MessageDelivery.h deleted file mode 100644 index cfde9ee307..0000000000 --- a/cpp/src/qpid/broker/MessageDelivery.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _broker_MessageDelivery_h -#define _broker_MessageDelivery_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 <boost/shared_ptr.hpp> -#include "DeliveryId.h" -#include "Consumer.h" -#include "qpid/framing/FrameHandler.h" - -namespace qpid { -namespace broker { - -class DeliveryToken; -class Message; -class Queue; - -/** - * TODO: clean this up; we don't need it anymore in its current form - * - * Encapsulates the different options for message delivery currently supported. - */ -class MessageDelivery { -public: - static boost::shared_ptr<DeliveryToken> getMessageDeliveryToken(const std::string& destination, - uint8_t confirmMode, - uint8_t acquireMode); - - static void deliver(QueuedMessage& msg, framing::FrameHandler& out, - DeliveryId deliveryTag, boost::shared_ptr<DeliveryToken> token, uint16_t framesize); -}; - -} -} - - -#endif /*!_broker_MessageDelivery_h*/ diff --git a/cpp/src/qpid/broker/MessageStore.h b/cpp/src/qpid/broker/MessageStore.h index 4c4c21dfba..143e860ec7 100644 --- a/cpp/src/qpid/broker/MessageStore.h +++ b/cpp/src/qpid/broker/MessageStore.h @@ -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 @@ -21,12 +21,12 @@ #ifndef _MessageStore_ #define _MessageStore_ -#include "PersistableExchange.h" -#include "PersistableMessage.h" -#include "PersistableQueue.h" -#include "PersistableConfig.h" -#include "RecoveryManager.h" -#include "TransactionalStore.h" +#include "qpid/broker/PersistableExchange.h" +#include "qpid/broker/PersistableMessage.h" +#include "qpid/broker/PersistableQueue.h" +#include "qpid/broker/PersistableConfig.h" +#include "qpid/broker/RecoveryManager.h" +#include "qpid/broker/TransactionalStore.h" #include "qpid/framing/FieldTable.h" #include <qpid/Options.h> @@ -46,12 +46,16 @@ class MessageStore : public TransactionalStore, public Recoverable { public: /** - * init the store, call before any other call. If not called, store - * is free to pick any defaults - * - * @param options Options object provided by concrete store plug in. + * If called after initialization but before recovery, will discard the database + * and reinitialize using an empty store dir. If the parameter pushDownStoreFiles + * is true, the content of the store dir will be moved to a backup dir inside the + * store dir. This is used when cluster nodes recover and must get thier content + * from a cluster sync rather than directly fromt the store. + * + * @param pushDownStoreFiles If true, will move content of the store dir into a + * subdir, leaving the store dir otherwise empty. */ - virtual bool init(const Options* options) = 0; + virtual void truncateInit(const bool pushDownStoreFiles = false) = 0; /** * Record the existence of a durable queue @@ -62,7 +66,7 @@ class MessageStore : public TransactionalStore, public Recoverable { * Destroy a durable queue */ virtual void destroy(PersistableQueue& queue) = 0; - + /** * Record the existence of a durable exchange */ @@ -72,17 +76,17 @@ class MessageStore : public TransactionalStore, public Recoverable { * Destroy a durable exchange */ virtual void destroy(const PersistableExchange& exchange) = 0; - + /** * Record a binding */ - virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, const std::string& key, const framing::FieldTable& args) = 0; /** * Forget a binding */ - virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, const std::string& key, const framing::FieldTable& args) = 0; /** @@ -102,10 +106,10 @@ class MessageStore : public TransactionalStore, public Recoverable { * point). If the message has not yet been stored it will * store the headers as well as any content passed in. A * persistence id will be set on the message which can be - * used to load the content or to append to it. + * used to load the content or to append to it. */ virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg) = 0; - + /** * Destroys a previously staged message. This only needs * to be called if the message is never enqueued. (Once @@ -119,7 +123,7 @@ class MessageStore : public TransactionalStore, public Recoverable { */ virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, const std::string& data) = 0; - + /** * Loads (a section) of content data for the specified * message (previously stored through a call to stage or @@ -128,18 +132,18 @@ class MessageStore : public TransactionalStore, public Recoverable { * content should be loaded, not the headers or related * meta-data). */ - virtual void loadContent(const qpid::broker::PersistableQueue& queue, + virtual void loadContent(const qpid::broker::PersistableQueue& queue, const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, uint64_t offset, uint32_t length) = 0; - + /** * Enqueues a message, storing the message if it has not * been previously stored and recording that the given - * message is on the given queue. + * message is on the given queue. * * Note: that this is async so the return of the function does * not mean the opperation is complete. - * + * * @param msg the message to enqueue * @param queue the name of the queue onto which it is to be enqueued * @param xid (a pointer to) an identifier of the @@ -149,7 +153,7 @@ class MessageStore : public TransactionalStore, public Recoverable { virtual void enqueue(TransactionContext* ctxt, const boost::intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& queue) = 0; - + /** * Dequeues a message, recording that the given message is * no longer on the given queue and deleting the message @@ -157,7 +161,7 @@ class MessageStore : public TransactionalStore, public Recoverable { * * Note: that this is async so the return of the function does * not mean the opperation is complete. - * + * * @param msg the message to dequeue * @param queue the name of the queue from which it is to be dequeued * @param xid (a pointer to) an identifier of the @@ -173,22 +177,22 @@ class MessageStore : public TransactionalStore, public Recoverable { * * Note: that this is async so the return of the function does * not mean the opperation is complete. - * + * * @param queue the name of the queue from which it is to be dequeued */ virtual void flush(const qpid::broker::PersistableQueue& queue)=0; /** * Returns the number of outstanding AIO's for a given queue - * - * If 0, than all the enqueue / dequeues have been stored + * + * If 0, than all the enqueue / dequeues have been stored * to disk * * @param queue the name of the queue to check for outstanding AIO */ virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue) = 0; - + virtual ~MessageStore(){} }; diff --git a/cpp/src/qpid/broker/MessageStoreModule.cpp b/cpp/src/qpid/broker/MessageStoreModule.cpp index c9528b9d98..5f7cceebd3 100644 --- a/cpp/src/qpid/broker/MessageStoreModule.cpp +++ b/cpp/src/qpid/broker/MessageStoreModule.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 @@ -19,25 +19,33 @@ * */ -#include "MessageStoreModule.h" +#include "qpid/broker/MessageStoreModule.h" +#include "qpid/broker/NullMessageStore.h" #include <iostream> // This transfer protects against the unloading of the store lib prior to the handling of the exception #define TRANSFER_EXCEPTION(fn) try { fn; } catch (std::exception& e) { throw Exception(e.what()); } using boost::intrusive_ptr; -using namespace qpid::broker; using qpid::framing::FieldTable; -MessageStoreModule::MessageStoreModule(MessageStore* _store) : store(_store) {} +namespace qpid { +namespace broker { + +MessageStoreModule::MessageStoreModule(boost::shared_ptr<MessageStore>& _store) + : store(_store) {} MessageStoreModule::~MessageStoreModule() { - delete store; } bool MessageStoreModule::init(const Options*) { return true; } +void MessageStoreModule::truncateInit(const bool pushDownStoreFiles) +{ + TRANSFER_EXCEPTION(store->truncateInit(pushDownStoreFiles)); +} + void MessageStoreModule::create(PersistableQueue& queue, const FieldTable& args) { TRANSFER_EXCEPTION(store->create(queue, args)); @@ -58,13 +66,13 @@ void MessageStoreModule::destroy(const PersistableExchange& exchange) TRANSFER_EXCEPTION(store->destroy(exchange)); } -void MessageStoreModule::bind(const PersistableExchange& e, const PersistableQueue& q, +void MessageStoreModule::bind(const PersistableExchange& e, const PersistableQueue& q, const std::string& k, const framing::FieldTable& a) { TRANSFER_EXCEPTION(store->bind(e, q, k, a)); } -void MessageStoreModule::unbind(const PersistableExchange& e, const PersistableQueue& q, +void MessageStoreModule::unbind(const PersistableExchange& e, const PersistableQueue& q, const std::string& k, const framing::FieldTable& a) { TRANSFER_EXCEPTION(store->unbind(e, q, k, a)); @@ -102,7 +110,7 @@ void MessageStoreModule::appendContent(const intrusive_ptr<const PersistableMess } void MessageStoreModule::loadContent( - const qpid::broker::PersistableQueue& queue, + const qpid::broker::PersistableQueue& queue, const intrusive_ptr<const PersistableMessage>& msg, string& data, uint64_t offset, uint32_t length) { @@ -162,3 +170,10 @@ void MessageStoreModule::collectPreparedXids(std::set<std::string>& xids) { TRANSFER_EXCEPTION(store->collectPreparedXids(xids)); } + +bool MessageStoreModule::isNull() const +{ + return NullMessageStore::isNullStore(store.get()); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/MessageStoreModule.h b/cpp/src/qpid/broker/MessageStoreModule.h index a16ef4de21..56b5a3c1ae 100644 --- a/cpp/src/qpid/broker/MessageStoreModule.h +++ b/cpp/src/qpid/broker/MessageStoreModule.h @@ -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 @@ -21,11 +21,12 @@ #ifndef _MessageStoreModule_ #define _MessageStoreModule_ -#include "MessageStore.h" -#include "Queue.h" -#include "RecoveryManager.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/RecoveryManager.h" #include <boost/intrusive_ptr.hpp> +#include <boost/shared_ptr.hpp> namespace qpid { namespace broker { @@ -35,11 +36,12 @@ namespace broker { */ class MessageStoreModule : public MessageStore { - MessageStore* store; + boost::shared_ptr<MessageStore> store; public: - MessageStoreModule(MessageStore* store); + MessageStoreModule(boost::shared_ptr<MessageStore>& store); bool init(const Options* options); + void truncateInit(const bool pushDownStoreFiles = false); std::auto_ptr<TransactionContext> begin(); std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); void prepare(TPCTransactionContext& txn); @@ -51,9 +53,9 @@ class MessageStoreModule : public MessageStore void destroy(PersistableQueue& queue); void create(const PersistableExchange& exchange, const framing::FieldTable& args); void destroy(const PersistableExchange& exchange); - void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + void bind(const PersistableExchange& exchange, const PersistableQueue& queue, const std::string& key, const framing::FieldTable& args); - void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, const std::string& key, const framing::FieldTable& args); void create(const PersistableConfig& config); void destroy(const PersistableConfig& config); @@ -61,7 +63,7 @@ class MessageStoreModule : public MessageStore void stage(const boost::intrusive_ptr<PersistableMessage>& msg); void destroy(PersistableMessage& msg); void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, const std::string& data); - void loadContent(const qpid::broker::PersistableQueue& queue, + void loadContent(const qpid::broker::PersistableQueue& queue, const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, uint64_t offset, uint32_t length); @@ -73,7 +75,8 @@ class MessageStoreModule : public MessageStore const PersistableQueue& queue); uint32_t outstandingQueueAIO(const PersistableQueue& queue); void flush(const qpid::broker::PersistableQueue& queue); - + bool isNull() const; + ~MessageStoreModule(); }; diff --git a/cpp/src/qpid/broker/NameGenerator.cpp b/cpp/src/qpid/broker/NameGenerator.cpp index 8484f921e9..e7f193d546 100644 --- a/cpp/src/qpid/broker/NameGenerator.cpp +++ b/cpp/src/qpid/broker/NameGenerator.cpp @@ -18,7 +18,7 @@ * under the License. * */ -#include "NameGenerator.h" +#include "qpid/broker/NameGenerator.h" #include <sstream> using namespace qpid::broker; diff --git a/cpp/src/qpid/broker/NullMessageStore.cpp b/cpp/src/qpid/broker/NullMessageStore.cpp index e1c7fe240d..6339b655f8 100644 --- a/cpp/src/qpid/broker/NullMessageStore.cpp +++ b/cpp/src/qpid/broker/NullMessageStore.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 @@ -19,9 +19,11 @@ * */ -#include "NullMessageStore.h" -#include "RecoveryManager.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/MessageStoreModule.h" +#include "qpid/broker/RecoveryManager.h" #include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" #include <iostream> @@ -32,47 +34,41 @@ namespace broker{ const std::string nullxid = ""; -class DummyCtxt : public TPCTransactionContext +class SimpleDummyCtxt : public TransactionContext {}; + +class DummyCtxt : public TPCTransactionContext { const std::string xid; public: DummyCtxt(const std::string& _xid) : xid(_xid) {} - static std::string getXid(TransactionContext& ctxt) + static std::string getXid(TransactionContext& ctxt) { DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt)); return c ? c->xid : nullxid; } }; -} +NullMessageStore::NullMessageStore() : nextPersistenceId(1) { + QPID_LOG(info, "No message store configured, persistence is disabled."); } -using namespace qpid::broker; - -NullMessageStore::NullMessageStore(bool _warn) : warn(_warn), nextPersistenceId(1) {} - bool NullMessageStore::init(const Options* /*options*/) {return true;} +void NullMessageStore::truncateInit(const bool /*pushDownStoreFiles*/) {} + void NullMessageStore::create(PersistableQueue& queue, const framing::FieldTable& /*args*/) { - QPID_LOG(info, "Queue '" << queue.getName() - << "' will not be durable. Persistence not enabled."); queue.setPersistenceId(nextPersistenceId++); } -void NullMessageStore::destroy(PersistableQueue&) -{ -} +void NullMessageStore::destroy(PersistableQueue&) {} void NullMessageStore::create(const PersistableExchange& exchange, const framing::FieldTable& /*args*/) { - QPID_LOG(info, "Exchange'" << exchange.getName() - << "' will not be durable. Persistence not enabled."); exchange.setPersistenceId(nextPersistenceId++); } -void NullMessageStore::destroy(const PersistableExchange& ) -{} +void NullMessageStore::destroy(const PersistableExchange& ) {} void NullMessageStore::bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){} @@ -80,47 +76,31 @@ void NullMessageStore::unbind(const PersistableExchange&, const PersistableQueue void NullMessageStore::create(const PersistableConfig& config) { - QPID_LOG(info, "Persistence not enabled, configuration not stored."); config.setPersistenceId(nextPersistenceId++); } -void NullMessageStore::destroy(const PersistableConfig&) -{ - QPID_LOG(info, "Persistence not enabled, configuration not stored."); -} +void NullMessageStore::destroy(const PersistableConfig&) {} -void NullMessageStore::recover(RecoveryManager&) -{ - QPID_LOG(info, "Persistence not enabled, no recovery attempted."); -} +void NullMessageStore::recover(RecoveryManager&) {} -void NullMessageStore::stage(const intrusive_ptr<PersistableMessage>&) -{ - QPID_LOG(info, "Can't stage message. Persistence not enabled."); -} +void NullMessageStore::stage(const intrusive_ptr<PersistableMessage>&) {} -void NullMessageStore::destroy(PersistableMessage&) -{ -} +void NullMessageStore::destroy(PersistableMessage&) {} -void NullMessageStore::appendContent(const intrusive_ptr<const PersistableMessage>&, const string&) -{ - QPID_LOG(info, "Can't append content. Persistence not enabled."); -} +void NullMessageStore::appendContent(const intrusive_ptr<const PersistableMessage>&, const string&) {} void NullMessageStore::loadContent(const qpid::broker::PersistableQueue&, const intrusive_ptr<const PersistableMessage>&, string&, uint64_t, uint32_t) { - QPID_LOG(info, "Can't load content. Persistence not enabled."); + throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled"); } void NullMessageStore::enqueue(TransactionContext*, const intrusive_ptr<PersistableMessage>& msg, - const PersistableQueue& queue) + const PersistableQueue&) { - msg->enqueueComplete(); - QPID_LOG(info, "Message is not durably recorded on '" << queue.getName() << "'. Persistence not enabled."); + msg->enqueueComplete(); } void NullMessageStore::dequeue(TransactionContext*, @@ -130,18 +110,15 @@ void NullMessageStore::dequeue(TransactionContext*, msg->dequeueComplete(); } -void NullMessageStore::flush(const qpid::broker::PersistableQueue&) -{ -} +void NullMessageStore::flush(const qpid::broker::PersistableQueue&) {} -uint32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& ) -{ +uint32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& ) { return 0; } std::auto_ptr<TransactionContext> NullMessageStore::begin() { - return std::auto_ptr<TransactionContext>(); + return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt()); } std::auto_ptr<TPCTransactionContext> NullMessageStore::begin(const std::string& xid) @@ -168,3 +145,21 @@ void NullMessageStore::collectPreparedXids(std::set<string>& out) { out.insert(prepared.begin(), prepared.end()); } + +bool NullMessageStore::isNull() const +{ + return true; +} + +bool NullMessageStore::isNullStore(const MessageStore* store) +{ + const MessageStoreModule* wrapper = dynamic_cast<const MessageStoreModule*>(store); + if (wrapper) { + return wrapper->isNull(); + } else { + const NullMessageStore* test = dynamic_cast<const NullMessageStore*>(store); + return test && test->isNull(); + } +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/NullMessageStore.h b/cpp/src/qpid/broker/NullMessageStore.h index 4b8d02d555..e148ec4d51 100644 --- a/cpp/src/qpid/broker/NullMessageStore.h +++ b/cpp/src/qpid/broker/NullMessageStore.h @@ -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 @@ -22,8 +22,9 @@ #define _NullMessageStore_ #include <set> -#include "MessageStore.h" -#include "Queue.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/Queue.h" #include <boost/intrusive_ptr.hpp> @@ -36,47 +37,58 @@ namespace broker { class NullMessageStore : public MessageStore { std::set<std::string> prepared; - const bool warn; uint64_t nextPersistenceId; public: - NullMessageStore(bool warn = false); + QPID_BROKER_EXTERN NullMessageStore(); - virtual bool init(const Options* options); - virtual std::auto_ptr<TransactionContext> begin(); - virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); - virtual void prepare(TPCTransactionContext& txn); - virtual void commit(TransactionContext& txn); - virtual void abort(TransactionContext& txn); - virtual void collectPreparedXids(std::set<std::string>& xids); + QPID_BROKER_EXTERN virtual bool init(const Options* options); + QPID_BROKER_EXTERN virtual void truncateInit(const bool pushDownStoreFiles = false); + QPID_BROKER_EXTERN virtual std::auto_ptr<TransactionContext> begin(); + QPID_BROKER_EXTERN virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); + QPID_BROKER_EXTERN virtual void prepare(TPCTransactionContext& txn); + QPID_BROKER_EXTERN virtual void commit(TransactionContext& txn); + QPID_BROKER_EXTERN virtual void abort(TransactionContext& txn); + QPID_BROKER_EXTERN virtual void collectPreparedXids(std::set<std::string>& xids); - virtual void create(PersistableQueue& queue, const framing::FieldTable& args); - virtual void destroy(PersistableQueue& queue); - virtual void create(const PersistableExchange& exchange, const framing::FieldTable& args); - virtual void destroy(const PersistableExchange& exchange); + QPID_BROKER_EXTERN virtual void create(PersistableQueue& queue, + const framing::FieldTable& args); + QPID_BROKER_EXTERN virtual void destroy(PersistableQueue& queue); + QPID_BROKER_EXTERN virtual void create(const PersistableExchange& exchange, + const framing::FieldTable& args); + QPID_BROKER_EXTERN virtual void destroy(const PersistableExchange& exchange); - virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, - const std::string& key, const framing::FieldTable& args); - virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, - const std::string& key, const framing::FieldTable& args); - virtual void create(const PersistableConfig& config); - virtual void destroy(const PersistableConfig& config); - virtual void recover(RecoveryManager& queues); - virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg); - virtual void destroy(PersistableMessage& msg); - virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, - const std::string& data); - virtual void loadContent(const qpid::broker::PersistableQueue& queue, - const boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, - uint64_t offset, uint32_t length); - virtual void enqueue(TransactionContext* ctxt, - const boost::intrusive_ptr<PersistableMessage>& msg, - const PersistableQueue& queue); - virtual void dequeue(TransactionContext* ctxt, - const boost::intrusive_ptr<PersistableMessage>& msg, - const PersistableQueue& queue); - virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue); - virtual void flush(const qpid::broker::PersistableQueue& queue); + QPID_BROKER_EXTERN virtual void bind(const PersistableExchange& exchange, + const PersistableQueue& queue, + const std::string& key, + const framing::FieldTable& args); + QPID_BROKER_EXTERN virtual void unbind(const PersistableExchange& exchange, + const PersistableQueue& queue, + const std::string& key, + const framing::FieldTable& args); + QPID_BROKER_EXTERN virtual void create(const PersistableConfig& config); + QPID_BROKER_EXTERN virtual void destroy(const PersistableConfig& config); + QPID_BROKER_EXTERN virtual void recover(RecoveryManager& queues); + QPID_BROKER_EXTERN virtual void stage(const boost::intrusive_ptr<PersistableMessage>& msg); + QPID_BROKER_EXTERN virtual void destroy(PersistableMessage& msg); + QPID_BROKER_EXTERN virtual void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg, + const std::string& data); + QPID_BROKER_EXTERN virtual void loadContent(const qpid::broker::PersistableQueue& queue, + const boost::intrusive_ptr<const PersistableMessage>& msg, + std::string& data, + uint64_t offset, + uint32_t length); + QPID_BROKER_EXTERN virtual void enqueue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + QPID_BROKER_EXTERN virtual void dequeue(TransactionContext* ctxt, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + QPID_BROKER_EXTERN virtual uint32_t outstandingQueueAIO(const PersistableQueue& queue); + QPID_BROKER_EXTERN virtual void flush(const qpid::broker::PersistableQueue& queue); ~NullMessageStore(){} + + QPID_BROKER_EXTERN virtual bool isNull() const; + static bool isNullStore(const MessageStore*); }; } diff --git a/cpp/src/qpid/broker/PersistableConfig.h b/cpp/src/qpid/broker/PersistableConfig.h index 914e91ea80..8ddb84d129 100644 --- a/cpp/src/qpid/broker/PersistableConfig.h +++ b/cpp/src/qpid/broker/PersistableConfig.h @@ -23,7 +23,7 @@ */ #include <string> -#include "Persistable.h" +#include "qpid/broker/Persistable.h" namespace qpid { namespace broker { diff --git a/cpp/src/qpid/broker/PersistableExchange.h b/cpp/src/qpid/broker/PersistableExchange.h index 683b740ddc..e1a0853247 100644 --- a/cpp/src/qpid/broker/PersistableExchange.h +++ b/cpp/src/qpid/broker/PersistableExchange.h @@ -23,7 +23,7 @@ */ #include <string> -#include "Persistable.h" +#include "qpid/broker/Persistable.h" namespace qpid { namespace broker { diff --git a/cpp/src/qpid/broker/PersistableMessage.cpp b/cpp/src/qpid/broker/PersistableMessage.cpp index 3bf390faf3..303a0501f4 100644 --- a/cpp/src/qpid/broker/PersistableMessage.cpp +++ b/cpp/src/qpid/broker/PersistableMessage.cpp @@ -20,12 +20,25 @@ */ -#include "PersistableMessage.h" -#include "MessageStore.h" +#include "qpid/broker/PersistableMessage.h" +#include "qpid/broker/MessageStore.h" #include <iostream> using namespace qpid::broker; +namespace qpid { +namespace broker { + +class MessageStore; + +PersistableMessage::~PersistableMessage() {} + +PersistableMessage::PersistableMessage() : + asyncEnqueueCounter(0), + asyncDequeueCounter(0), + store(0) +{} + void PersistableMessage::flush() { syncList copy; @@ -45,4 +58,126 @@ void PersistableMessage::flush() } } +void PersistableMessage::setContentReleased() +{ + contentReleaseState.released = true; +} + +bool PersistableMessage::isContentReleased() const +{ + return contentReleaseState.released; +} + +bool PersistableMessage::isEnqueueComplete() { + sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); + return asyncEnqueueCounter == 0; +} + +void PersistableMessage::enqueueComplete() { + bool notify = false; + { + sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); + if (asyncEnqueueCounter > 0) { + if (--asyncEnqueueCounter == 0) { + notify = true; + } + } + } + if (notify) { + allEnqueuesComplete(); + sys::ScopedLock<sys::Mutex> l(storeLock); + if (store) { + for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) { + PersistableQueue::shared_ptr q(i->lock()); + if (q) q->notifyDurableIOComplete(); + } + } + } +} + +bool PersistableMessage::isStoredOnQueue(PersistableQueue::shared_ptr queue){ + if (store && (queue->getPersistenceId()!=0)) { + for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) { + PersistableQueue::shared_ptr q(i->lock()); + if (q && q->getPersistenceId() == queue->getPersistenceId()) return true; + } + } + return false; +} + + +void PersistableMessage::addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store) { + if (_store){ + sys::ScopedLock<sys::Mutex> l(storeLock); + store = _store; + boost::weak_ptr<PersistableQueue> q(queue); + synclist.push_back(q); + } +} + +void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { + addToSyncList(queue, _store); + enqueueAsync(); +} + +void PersistableMessage::enqueueAsync() { + sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock); + asyncEnqueueCounter++; +} + +bool PersistableMessage::isDequeueComplete() { + sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); + return asyncDequeueCounter == 0; +} +void PersistableMessage::dequeueComplete() { + bool notify = false; + { + sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); + if (asyncDequeueCounter > 0) { + if (--asyncDequeueCounter == 0) { + notify = true; + } + } + } + if (notify) allDequeuesComplete(); +} + +void PersistableMessage::dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { + if (_store){ + sys::ScopedLock<sys::Mutex> l(storeLock); + store = _store; + boost::weak_ptr<PersistableQueue> q(queue); + synclist.push_back(q); + } + dequeueAsync(); +} + +void PersistableMessage::dequeueAsync() { + sys::ScopedLock<sys::Mutex> l(asyncDequeueLock); + asyncDequeueCounter++; +} + +PersistableMessage::ContentReleaseState::ContentReleaseState() : blocked(false), requested(false), released(false) {} + +void PersistableMessage::setStore(MessageStore* s) +{ + store = s; +} + +void PersistableMessage::requestContentRelease() +{ + contentReleaseState.requested = true; +} +void PersistableMessage::blockContentRelease() +{ + contentReleaseState.blocked = true; +} +bool PersistableMessage::checkContentReleasable() +{ + return contentReleaseState.requested && !contentReleaseState.blocked; +} + +}} + + diff --git a/cpp/src/qpid/broker/PersistableMessage.h b/cpp/src/qpid/broker/PersistableMessage.h index 7ed54c0ff0..7d49491dfd 100644 --- a/cpp/src/qpid/broker/PersistableMessage.h +++ b/cpp/src/qpid/broker/PersistableMessage.h @@ -26,10 +26,11 @@ #include <list> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> -#include "Persistable.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Persistable.h" #include "qpid/framing/amqp_types.h" -#include "qpid/sys/Monitor.h" -#include "PersistableQueue.h" +#include "qpid/sys/Mutex.h" +#include "qpid/broker/PersistableQueue.h" namespace qpid { namespace broker { @@ -41,10 +42,11 @@ class MessageStore; */ class PersistableMessage : public Persistable { - sys::Monitor asyncEnqueueLock; - sys::Monitor asyncDequeueLock; + typedef std::list< boost::weak_ptr<PersistableQueue> > syncList; + sys::Mutex asyncEnqueueLock; + sys::Mutex asyncDequeueLock; sys::Mutex storeLock; - + /** * Tracks the number of outstanding asynchronous enqueue * operations. When the message is enqueued asynchronously the @@ -62,15 +64,33 @@ class PersistableMessage : public Persistable * dequeues. */ int asyncDequeueCounter; -protected: - typedef std::list< boost::weak_ptr<PersistableQueue> > syncList; + + void enqueueAsync(); + void dequeueAsync(); + syncList synclist; + struct ContentReleaseState + { + bool blocked; + bool requested; + bool released; + + ContentReleaseState(); + }; + ContentReleaseState contentReleaseState; + + protected: + /** Called when all enqueues are complete for this message. */ + virtual void allEnqueuesComplete() = 0; + /** Called when all dequeues are complete for this message. */ + virtual void allDequeuesComplete() = 0; + + void setContentReleased(); + MessageStore* store; - bool contentReleased; - - inline void setContentReleased() {contentReleased = true; } -public: + + public: typedef boost::shared_ptr<PersistableMessage> shared_ptr; /** @@ -78,105 +98,39 @@ public: */ virtual uint32_t encodedHeaderSize() const = 0; - virtual ~PersistableMessage() {}; + virtual ~PersistableMessage(); - PersistableMessage(): - asyncEnqueueCounter(0), - asyncDequeueCounter(0), - store(0), - contentReleased(false) - {} + PersistableMessage(); void flush(); - inline bool isContentReleased()const {return contentReleased; } - - inline void waitForEnqueueComplete() { - sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); - while (asyncEnqueueCounter > 0) { - asyncEnqueueLock.wait(); - } - } - - inline bool isEnqueueComplete() { - sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); - return asyncEnqueueCounter == 0; - } - - inline void enqueueComplete() { - bool notify = false; - { - sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); - if (asyncEnqueueCounter > 0) { - if (--asyncEnqueueCounter == 0) { - asyncEnqueueLock.notify(); - notify = true; - } - } - } - if (notify) { - sys::ScopedLock<sys::Mutex> l(storeLock); - if (store) { - for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) { - PersistableQueue::shared_ptr q(i->lock()); - if (q) q->notifyDurableIOComplete(); - } - } - } - } - - inline void enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { - if (_store){ - sys::ScopedLock<sys::Mutex> l(storeLock); - store = _store; - boost::weak_ptr<PersistableQueue> q(queue); - synclist.push_back(q); - } - enqueueAsync(); - } - - inline void enqueueAsync() { - sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); - asyncEnqueueCounter++; - } - - inline bool isDequeueComplete() { - sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); - return asyncDequeueCounter == 0; - } + QPID_BROKER_EXTERN bool isContentReleased() const; + + QPID_BROKER_EXTERN void setStore(MessageStore*); + void requestContentRelease(); + void blockContentRelease(); + bool checkContentReleasable(); + + virtual QPID_BROKER_EXTERN bool isPersistent() const = 0; + + QPID_BROKER_EXTERN bool isEnqueueComplete(); + + QPID_BROKER_EXTERN void enqueueComplete(); + + QPID_BROKER_EXTERN void enqueueAsync(PersistableQueue::shared_ptr queue, + MessageStore* _store); + + + QPID_BROKER_EXTERN bool isDequeueComplete(); - inline void dequeueComplete() { - - sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); - if (asyncDequeueCounter > 0) { - if (--asyncDequeueCounter == 0) { - asyncDequeueLock.notify(); - } - } - } - - inline void waitForDequeueComplete() { - sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); - while (asyncDequeueCounter > 0) { - asyncDequeueLock.wait(); - } - } - - inline void dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { - if (_store){ - sys::ScopedLock<sys::Mutex> l(storeLock); - store = _store; - boost::weak_ptr<PersistableQueue> q(queue); - synclist.push_back(q); - } - dequeueAsync(); - } - - inline void dequeueAsync() { - sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); - asyncDequeueCounter++; - } + QPID_BROKER_EXTERN void dequeueComplete(); + QPID_BROKER_EXTERN void dequeueAsync(PersistableQueue::shared_ptr queue, + MessageStore* _store); + + bool isStoredOnQueue(PersistableQueue::shared_ptr queue); + + void addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store); }; diff --git a/cpp/src/qpid/broker/PersistableQueue.h b/cpp/src/qpid/broker/PersistableQueue.h index 9236814ae3..8d85d36fef 100644 --- a/cpp/src/qpid/broker/PersistableQueue.h +++ b/cpp/src/qpid/broker/PersistableQueue.h @@ -23,7 +23,7 @@ */ #include <string> -#include "Persistable.h" +#include "qpid/broker/Persistable.h" #include "qpid/management/Manageable.h" #include <boost/shared_ptr.hpp> diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp index 40dfb80da2..f4231f2397 100644 --- a/cpp/src/qpid/broker/Queue.cpp +++ b/cpp/src/qpid/broker/Queue.cpp @@ -19,19 +19,23 @@ * */ -#include "Broker.h" -#include "Queue.h" -#include "Exchange.h" -#include "DeliverableMessage.h" -#include "MessageStore.h" -#include "QueueRegistry.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/QueueEvents.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/QueueRegistry.h" #include "qpid/StringUtils.h" #include "qpid/log/Statement.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/FieldTable.h" #include "qpid/sys/Monitor.h" #include "qpid/sys/Time.h" -#include "qpid/management/ArgsQueuePurge.h" +#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h" #include <iostream> #include <algorithm> @@ -49,11 +53,34 @@ using qpid::management::Manageable; using qpid::management::Args; using std::for_each; using std::mem_fun; +namespace _qmf = qmf::org::apache::qpid::broker; + + +namespace +{ +const std::string qpidMaxSize("qpid.max_size"); +const std::string qpidMaxCount("qpid.max_count"); +const std::string qpidNoLocal("no-local"); +const std::string qpidTraceIdentity("qpid.trace.id"); +const std::string qpidTraceExclude("qpid.trace.exclude"); +const std::string qpidLastValueQueue("qpid.last_value_queue"); +const std::string qpidLastValueQueueNoBrowse("qpid.last_value_queue_no_browse"); +const std::string qpidPersistLastNode("qpid.persist_last_node"); +const std::string qpidVQMatchProperty("qpid.LVQ_key"); +const std::string qpidQueueEventGeneration("qpid.queue_event_generation"); +//following feature is not ready for general use as it doesn't handle +//the case where a message is enqueued on more than one queue well enough: +const std::string qpidInsertSequenceNumbers("qpid.insert_sequence_numbers"); + +const int ENQUEUE_ONLY=1; +const int ENQUEUE_AND_DEQUEUE=2; +} Queue::Queue(const string& _name, bool _autodelete, MessageStore* const _store, const OwnershipToken* const _owner, - Manageable* parent) : + Manageable* parent, + Broker* b) : name(_name), autodelete(_autodelete), @@ -62,22 +89,31 @@ Queue::Queue(const string& _name, bool _autodelete, consumerCount(0), exclusive(0), noLocal(false), + lastValueQueue(false), + lastValueQueueNoBrowse(false), + persistLastNode(false), + inLastNodeFailure(false), persistenceId(0), policyExceeded(false), - mgmtObject(0) + mgmtObject(0), + eventMode(0), + eventMgr(0), + insertSeqNo(0), + broker(b) { - if (parent != 0) + if (parent != 0 && broker != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtObject = new management::Queue (agent, this, parent, _name, _store != 0, _autodelete, _owner != 0); + mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete, _owner != 0); // Add the object to the management agent only if this queue is not durable. // If it's durable, we will add it later when the queue is assigned a persistenceId. - if (store == 0) - agent->addObject (mgmtObject); + if (store == 0) { + agent->addObject (mgmtObject, agent->allocateId(this)); + } } } } @@ -90,8 +126,12 @@ Queue::~Queue() void Queue::notifyDurableIOComplete() { - Mutex::ScopedLock locker(messageLock); - notify(); + QueueListeners::NotificationSet copy; + { + Mutex::ScopedLock locker(messageLock); + listeners.populate(copy); + } + copy.notify(); } bool isLocalTo(const OwnershipToken* token, boost::intrusive_ptr<Message>& msg) @@ -129,35 +169,31 @@ void Queue::deliver(boost::intrusive_ptr<Message>& msg){ } else { // if no store then mark as enqueued if (!enqueue(0, msg)){ - if (mgmtObject != 0) { - mgmtObject->inc_msgTotalEnqueues (); - mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - } push(msg); msg->enqueueComplete(); }else { - if (mgmtObject != 0) { - mgmtObject->inc_msgTotalEnqueues (); - mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - mgmtObject->inc_msgPersistEnqueues (); - mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); - } push(msg); } + mgntEnqStats(msg); QPID_LOG(debug, "Message " << msg << " enqueued on " << name << "[" << this << "]"); } } +void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg) +{ + if (policy.get()) policy->recoverEnqueued(msg); +} void Queue::recover(boost::intrusive_ptr<Message>& msg){ - push(msg); - msg->enqueueComplete(); // mark the message as enqueued - if (mgmtObject != 0) { - mgmtObject->inc_msgTotalEnqueues (); - mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); - mgmtObject->inc_msgPersistEnqueues (); - mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); + if (policy.get()) policy->recoverEnqueued(msg); + + push(msg, true); + if (store){ + // setup synclist for recovered messages, so they don't get re-stored on lastNodeFailure + msg->addToSyncList(shared_from_this(), store); } + msg->enqueueComplete(); // mark the message as enqueued + mgntEnqStats(msg); if (store && !msg->isContentLoaded()) { //content has not been loaded, need to ensure that lazy loading mode is set: @@ -168,121 +204,188 @@ void Queue::recover(boost::intrusive_ptr<Message>& msg){ void Queue::process(boost::intrusive_ptr<Message>& msg){ push(msg); - if (mgmtObject != 0) { - mgmtObject->inc_msgTotalEnqueues (); - mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + mgntEnqStats(msg); + if (mgmtObject != 0){ mgmtObject->inc_msgTxnEnqueues (); mgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); - if (msg->isPersistent ()) { - mgmtObject->inc_msgPersistEnqueues (); - mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); - } } } void Queue::requeue(const QueuedMessage& msg){ + QueueListeners::NotificationSet copy; + { + Mutex::ScopedLock locker(messageLock); + if (!isEnqueued(msg)) return; + msg.payload->enqueueComplete(); // mark the message as enqueued + messages.insert(lower_bound(messages.begin(), messages.end(), msg), msg); + listeners.populate(copy); + + // for persistLastNode - don't force a message twice to disk, but force it if no force before + if(inLastNodeFailure && persistLastNode && !msg.payload->isStoredOnQueue(shared_from_this())) { + msg.payload->forcePersistent(); + if (msg.payload->isForcedPersistent() ){ + enqueue(0, msg.payload); + } + } + } + copy.notify(); +} + +void Queue::clearLVQIndex(const QueuedMessage& msg){ + const framing::FieldTable* ft = msg.payload ? msg.payload->getApplicationHeaders() : 0; + if (lastValueQueue && ft){ + string key = ft->getAsString(qpidVQMatchProperty); + lvq.erase(key); + } +} + +bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message) +{ Mutex::ScopedLock locker(messageLock); - msg.payload->enqueueComplete(); // mark the message as enqueued - messages.push_front(msg); - notify(); + QPID_LOG(debug, "Attempting to acquire message at " << position); + + Messages::iterator i = findAt(position); + if (i != messages.end() ) { + message = *i; + if (lastValueQueue) { + clearLVQIndex(*i); + } + QPID_LOG(debug, + "Acquired message at " << i->position << " from " << name); + messages.erase(i); + return true; + } + QPID_LOG(debug, "Could not acquire message at " << position << " from " << name << "; no message at that position"); + return false; } bool Queue::acquire(const QueuedMessage& msg) { Mutex::ScopedLock locker(messageLock); QPID_LOG(debug, "attempting to acquire " << msg.position); - for (Messages::iterator i = messages.begin(); i != messages.end(); i++) { - if (i->position == msg.position) { - messages.erase(i); - QPID_LOG(debug, "Match found, acquire succeeded: " << i->position << " == " << msg.position); - return true; - } else { - QPID_LOG(debug, "No match: " << i->position << " != " << msg.position); - } + Messages::iterator i = findAt(msg.position); + if ((i != messages.end() && !lastValueQueue) // note that in some cases payload not be set + || (lastValueQueue && (i->position == msg.position) && + msg.payload.get() == checkLvqReplace(*i).payload.get()) ) { + + clearLVQIndex(msg); + QPID_LOG(debug, + "Match found, acquire succeeded: " << + i->position << " == " << msg.position); + messages.erase(i); + return true; + } else { + QPID_LOG(debug, "No match: " << i->position << " != " << msg.position); } + QPID_LOG(debug, "Acquire failed for " << msg.position); return false; } -bool Queue::getNextMessage(QueuedMessage& m, Consumer& c) +void Queue::notifyListener() { - if (c.preAcquires()) { - return consumeNextMessage(m, c); + QueueListeners::NotificationSet set; + { + Mutex::ScopedLock locker(messageLock); + if (messages.size()) { + listeners.populate(set); + } + } + set.notify(); +} + +bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr c) +{ + if (c->preAcquires()) { + switch (consumeNextMessage(m, c)) { + case CONSUMED: + return true; + case CANT_CONSUME: + notifyListener();//let someone else try + case NO_MESSAGES: + default: + return false; + } } else { return browseNextMessage(m, c); } } -bool Queue::checkForMessages(Consumer& c) +bool Queue::checkForMessages(Consumer::shared_ptr c) { Mutex::ScopedLock locker(messageLock); if (messages.empty()) { //no message available, register consumer for notification //when this changes - addListener(c); + listeners.addListener(c); return false; } else { - QueuedMessage msg = messages.front(); + QueuedMessage msg = getFront(); if (store && !msg.payload->isEnqueueComplete()) { //though a message is on the queue, it has not yet been //enqueued and so is not available for consumption yet, //register consumer for notification when this changes - addListener(c); + listeners.addListener(c); return false; } else { //check that consumer has sufficient credit for the //message (if it does not, no need to register it for //notification as the consumer itself will handle the //credit allocation required to change this condition). - return c.accept(msg.payload); + return c->accept(msg.payload); } } } -bool Queue::consumeNextMessage(QueuedMessage& m, Consumer& c) +Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr c) { while (true) { Mutex::ScopedLock locker(messageLock); if (messages.empty()) { QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); - addListener(c); - return false; + listeners.addListener(c); + return NO_MESSAGES; } else { - QueuedMessage msg = messages.front(); - if (store && !msg.payload->isEnqueueComplete()) { - QPID_LOG(debug, "Messages not ready to dispatch on queue '" << name << "'"); - addListener(c); - return false; + QueuedMessage msg = getFront(); + if (msg.payload->hasExpired()) { + QPID_LOG(debug, "Message expired from queue '" << name << "'"); + popAndDequeue(); + continue; } - - if (c.filter(msg.payload)) { - if (c.accept(msg.payload)) { + + if (c->filter(msg.payload)) { + if (c->accept(msg.payload)) { m = msg; - messages.pop_front(); - return true; + popMsg(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 << "'"); - return false; + return CANT_CONSUME; } } else { //consumer will never want this message QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); - return false; + return CANT_CONSUME; } } } } -bool Queue::browseNextMessage(QueuedMessage& m, Consumer& c) +bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr c) { QueuedMessage msg(this); while (seek(msg, c)) { - if (c.filter(msg.payload)) { - if (c.accept(msg.payload)) { + if (c->filter(msg.payload) && !msg.payload->hasExpired()) { + if (c->accept(msg.payload)) { //consumer wants the message - c.position = msg.position; + c->position = msg.position; m = msg; + if (!lastValueQueueNoBrowse) clearLVQIndex(msg); + if (lastValueQueue) { + boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this); + if (replacement.get()) m.payload = replacement; + } return true; } else { //browser hasn't got enough credit for the message @@ -291,70 +394,89 @@ bool Queue::browseNextMessage(QueuedMessage& m, Consumer& c) } } else { //consumer will never want this message, continue seeking - c.position = msg.position; + c->position = msg.position; QPID_LOG(debug, "Browser skipping message from '" << name << "'"); } } return false; } -/** - * notify listeners that there may be messages to process - */ -void Queue::notify() -{ - if (listeners.empty()) return; - - Listeners copy(listeners); - listeners.clear(); - for_each(copy.begin(), copy.end(), mem_fun(&Consumer::notify)); -} - -void Queue::removeListener(Consumer& c) -{ - Mutex::ScopedLock locker(messageLock); - Listeners::iterator i = std::find(listeners.begin(), listeners.end(), &c); - if (i != listeners.end()) listeners.erase(i); -} - -void Queue::addListener(Consumer& c) +void Queue::removeListener(Consumer::shared_ptr c) { - Listeners::iterator i = std::find(listeners.begin(), listeners.end(), &c); - if (i == listeners.end()) listeners.push_back(&c); + QueueListeners::NotificationSet set; + { + Mutex::ScopedLock locker(messageLock); + listeners.removeListener(c); + if (messages.size()) { + listeners.populate(set); + } + } + set.notify(); } -bool Queue::dispatch(Consumer& c) +bool Queue::dispatch(Consumer::shared_ptr c) { QueuedMessage msg(this); if (getNextMessage(msg, c)) { - c.deliver(msg); + c->deliver(msg); return true; } else { return false; } } -bool Queue::seek(QueuedMessage& msg, Consumer& c) { +// Find the next message +bool Queue::seek(QueuedMessage& msg, Consumer::shared_ptr c) { Mutex::ScopedLock locker(messageLock); - if (!messages.empty() && messages.back().position > c.position) { - if (c.position < messages.front().position) { - msg = messages.front(); + if (!messages.empty() && messages.back().position > c->position) { + if (c->position < getFront().position) { + msg = getFront(); return true; } else { - //TODO: can improve performance of this search, for now just searching linearly from end - Messages::reverse_iterator pos; - for (Messages::reverse_iterator i = messages.rbegin(); i != messages.rend() && i->position > c.position; i++) { - pos = i; + Messages::iterator pos = findAt(c->position); + if (pos != messages.end() && pos+1 != messages.end()) { + msg = *(pos+1); + return true; } - msg = *pos; - return true; } } - addListener(c); + listeners.addListener(c); return false; } -void Queue::consume(Consumer& c, bool requestExclusive){ +Queue::Messages::iterator Queue::findAt(SequenceNumber pos) { + + if(!messages.empty()){ + QueuedMessage compM; + compM.position = pos; + unsigned long diff = pos.getValue() - messages.front().position.getValue(); + long maxEnd = diff < messages.size()? diff : messages.size(); + + Messages::iterator i = lower_bound(messages.begin(),messages.begin()+maxEnd,compM); + if (i!= messages.end() && i->position == pos) + return i; + } + return messages.end(); // no match found. +} + + +QueuedMessage Queue::find(SequenceNumber pos) const { + + Mutex::ScopedLock locker(messageLock); + if(!messages.empty()){ + QueuedMessage compM; + compM.position = pos; + unsigned long diff = pos.getValue() - messages.front().position.getValue(); + long maxEnd = diff < messages.size()? diff : messages.size(); + + Messages::const_iterator i = lower_bound(messages.begin(),messages.begin()+maxEnd,compM); + if (i != messages.end()) + return *i; + } + return QueuedMessage(); +} + +void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){ Mutex::ScopedLock locker(consumerLock); if(exclusive) { throw ResourceLockedException( @@ -364,7 +486,7 @@ void Queue::consume(Consumer& c, bool requestExclusive){ throw ResourceLockedException( QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied.")); } else { - exclusive = c.getSession(); + exclusive = c->getSession(); } } consumerCount++; @@ -372,7 +494,7 @@ void Queue::consume(Consumer& c, bool requestExclusive){ mgmtObject->inc_consumerCount (); } -void Queue::cancel(Consumer& c){ +void Queue::cancel(Consumer::shared_ptr c){ removeListener(c); Mutex::ScopedLock locker(consumerLock); consumerCount--; @@ -386,12 +508,35 @@ QueuedMessage Queue::get(){ QueuedMessage msg(this); if(!messages.empty()){ - msg = messages.front(); - messages.pop_front(); + msg = getFront(); + popMsg(msg); } return msg; } +void Queue::purgeExpired() +{ + //As expired messages are discarded during dequeue also, only + //bother explicitly expiring if the rate of dequeues since last + //attempt is less than one per second. + if (dequeueTracker.sampleRatePerSecond() < 1) { + Messages expired; + { + Mutex::ScopedLock locker(messageLock); + for (Messages::iterator i = messages.begin(); i != messages.end();) { + if (lastValueQueue) checkLvqReplace(*i); + if (i->payload->hasExpired()) { + expired.push_back(*i); + i = messages.erase(i); + } else { + ++i; + } + } + } + for_each(expired.begin(), expired.end(), bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); + } +} + /** * purge - for purging all or some messages on a queue * depending on the purge_request @@ -406,106 +551,252 @@ uint32_t Queue::purge(const uint32_t purge_request){ uint32_t count = 0; // Either purge them all or just the some (purge_count) while the queue isn't empty. - while((!purge_request || purge_count--) && !messages.empty()) - { + while((!purge_request || purge_count--) && !messages.empty()) { popAndDequeue(); - count++; + count++; } return count; } -void Queue::push(boost::intrusive_ptr<Message>& msg){ - Mutex::ScopedLock locker(messageLock); - messages.push_back(QueuedMessage(this, msg, ++sequence)); - if (policy.get()) { - policy->enqueued(msg->contentSize()); - if (policy->limitExceeded()) { - if (!policyExceeded) { - policyExceeded = true; - QPID_LOG(info, "Queue size exceeded policy for " << name); - } - if (store) { - QPID_LOG(debug, "Message " << msg << " on " << name << " released from memory"); - msg->releaseContent(store); - } else { - QPID_LOG(error, "Message " << msg << " on " << name - << " exceeds the policy for the queue but can't be released from memory as the queue is not durable"); - throw ResourceLimitExceededException(QPID_MSG("Policy exceeded for " << name << " " << *policy)); - } - } else { - if (policyExceeded) { - policyExceeded = false; - QPID_LOG(info, "Queue size within policy for " << name); - } +uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty) { + Mutex::ScopedLock locker(messageLock); + uint32_t move_count = qty; // only comes into play if qty >0 + uint32_t count = 0; // count how many were moved for returning + + while((!qty || move_count--) && !messages.empty()) { + QueuedMessage qmsg = getFront(); + boost::intrusive_ptr<Message> msg = qmsg.payload; + destq->deliver(msg); // deliver message to the destination queue + popMsg(qmsg); + dequeue(0, qmsg); + count++; + } + return count; +} + +void Queue::popMsg(QueuedMessage& qmsg) +{ + const framing::FieldTable* ft = qmsg.payload->getApplicationHeaders(); + if (lastValueQueue && ft){ + string key = ft->getAsString(qpidVQMatchProperty); + lvq.erase(key); + } + messages.pop_front(); + ++dequeueTracker; +} + +void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){ + QueueListeners::NotificationSet copy; + { + Mutex::ScopedLock locker(messageLock); + QueuedMessage qm(this, msg, ++sequence); + if (insertSeqNo) msg->getOrInsertHeaders().setInt64(seqNoKey, sequence); + + LVQ::iterator i; + const framing::FieldTable* ft = msg->getApplicationHeaders(); + if (lastValueQueue && ft){ + string key = ft->getAsString(qpidVQMatchProperty); + + i = lvq.find(key); + if (i == lvq.end() || (broker && broker->isClusterUpdatee())) { + messages.push_back(qm); + listeners.populate(copy); + lvq[key] = msg; + }else { + boost::intrusive_ptr<Message> old = i->second->getReplacementMessage(this); + if (!old) old = i->second; + i->second->setReplacementMessage(msg,this); + if (isRecovery) { + //can't issue new requests for the store until + //recovery is complete + pendingDequeues.push_back(QueuedMessage(qm.queue, old, qm.position)); + } else { + Mutex::ScopedUnlock u(messageLock); + dequeue(0, QueuedMessage(qm.queue, old, qm.position)); + } + } + }else { + messages.push_back(qm); + listeners.populate(copy); + } + if (eventMode) { + if (eventMgr) eventMgr->enqueued(qm); + else QPID_LOG(warning, "Enqueue manager not set, events not generated for " << getName()); + } + if (policy.get()) { + policy->enqueued(qm); } } - notify(); + copy.notify(); +} + +QueuedMessage Queue::getFront() +{ + QueuedMessage msg = messages.front(); + if (lastValueQueue) { + boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this); + if (replacement.get()) msg.payload = replacement; + } + return msg; +} + +QueuedMessage& Queue::checkLvqReplace(QueuedMessage& msg) +{ + boost::intrusive_ptr<Message> replacement = msg.payload->getReplacementMessage(this); + if (replacement.get()) { + const framing::FieldTable* ft = replacement->getApplicationHeaders(); + if (ft) { + string key = ft->getAsString(qpidVQMatchProperty); + if (lvq.find(key) != lvq.end()){ + lvq[key] = replacement; + } + } + msg.payload = replacement; + } + return msg; } /** function only provided for unit tests, or code not in critical message path */ -uint32_t Queue::getMessageCount() const{ +uint32_t Queue::getEnqueueCompleteMessageCount() const +{ Mutex::ScopedLock locker(messageLock); - - uint32_t count =0; + uint32_t count = 0; for ( Messages::const_iterator i = messages.begin(); i != messages.end(); ++i ) { + //NOTE: don't need to use checkLvqReplace() here as it + //is only relevant for LVQ which does not support persistence + //so the enqueueComplete check has no effect if ( i->payload->isEnqueueComplete() ) count ++; } return count; } -uint32_t Queue::getConsumerCount() const{ +uint32_t Queue::getMessageCount() const +{ + Mutex::ScopedLock locker(messageLock); + return messages.size(); +} + +uint32_t Queue::getConsumerCount() const +{ Mutex::ScopedLock locker(consumerLock); return consumerCount; } -bool Queue::canAutoDelete() const{ +bool Queue::canAutoDelete() const +{ Mutex::ScopedLock locker(consumerLock); return autodelete && !consumerCount; } +void Queue::clearLastNodeFailure() +{ + inLastNodeFailure = false; +} + +void Queue::setLastNodeFailure() +{ + if (persistLastNode){ + Mutex::ScopedLock locker(messageLock); + try { + for ( Messages::iterator i = messages.begin(); i != messages.end(); ++i ) { + if (lastValueQueue) checkLvqReplace(*i); + // don't force a message twice to disk. + if(!i->payload->isStoredOnQueue(shared_from_this())) { + i->payload->forcePersistent(); + if (i->payload->isForcedPersistent() ){ + enqueue(0, i->payload); + } + } + } + } catch (const std::exception& e) { + // Could not go into last node standing (for example journal not large enough) + QPID_LOG(error, "Unable to fail to last node standing for queue: " << name << " : " << e.what()); + } + inLastNodeFailure = true; + } +} + // return true if store exists, -bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg) +bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg, bool suppressPolicyCheck) { + if (policy.get() && !suppressPolicyCheck) { + Messages dequeues; + { + Mutex::ScopedLock locker(messageLock); + policy->tryEnqueue(msg); + policy->getPendingDequeues(dequeues); + } + //depending on policy, may have some dequeues that need to performed without holding the lock + for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); + } + + if (inLastNodeFailure && persistLastNode){ + msg->forcePersistent(); + } + if (traceId.size()) { msg->addTraceId(traceId); } - if (msg->isPersistent() && store) { + if ((msg->isPersistent() || msg->checkContentReleasable()) && store) { msg->enqueueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg); store->enqueue(ctxt, pmsg, *this); return true; } - //msg->enqueueAsync(); // increments intrusive ptr cnt + if (!store) { + //Messages enqueued on a transient queue should be prevented + //from having their content released as it may not be + //recoverable by these queue for delivery + msg->blockContentRelease(); + } return false; } +void Queue::enqueueAborted(boost::intrusive_ptr<Message> msg) +{ + Mutex::ScopedLock locker(messageLock); + if (policy.get()) policy->enqueueAborted(msg); +} + // return true if store exists, -bool Queue::dequeue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg) +bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg) { { Mutex::ScopedLock locker(messageLock); - dequeued(msg); + if (!isEnqueued(msg)) return false; + if (!ctxt) { + dequeued(msg); + } } - if (msg->isPersistent() && store) { - msg->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue - boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg); + if ((msg.payload->isPersistent() || msg.payload->checkContentReleasable()) && store) { + msg.payload->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue + boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg.payload); store->dequeue(ctxt, pmsg, *this); return true; } - //msg->dequeueAsync(); // decrements intrusive ptr cnt return false; } +void Queue::dequeueCommitted(const QueuedMessage& msg) +{ + Mutex::ScopedLock locker(messageLock); + dequeued(msg); + if (mgmtObject != 0) { + mgmtObject->inc_msgTxnDequeues(); + mgmtObject->inc_byteTxnDequeues(msg.payload->contentSize()); + } +} + /** * Removes a message from the in-memory delivery queue as well * dequeing it from the logical (and persistent if applicable) queue */ void Queue::popAndDequeue() { - boost::intrusive_ptr<Message> msg = messages.front().payload; - messages.pop_front(); + QueuedMessage msg = getFront(); + popMsg(msg); dequeue(0, msg); } @@ -513,29 +804,16 @@ void Queue::popAndDequeue() * Updates policy and management when a message has been dequeued, * expects messageLock to be held */ -void Queue::dequeued(boost::intrusive_ptr<Message>& msg) +void Queue::dequeued(const QueuedMessage& msg) { - if (policy.get()) policy->dequeued(msg->contentSize()); - if (mgmtObject != 0){ - mgmtObject->inc_msgTotalDequeues (); - mgmtObject->inc_byteTotalDequeues (msg->contentSize()); - if (msg->isPersistent ()){ - mgmtObject->inc_msgPersistDequeues (); - mgmtObject->inc_bytePersistDequeues (msg->contentSize()); - } + if (policy.get()) policy->dequeued(msg); + mgntDeqStats(msg.payload); + if (eventMode == ENQUEUE_AND_DEQUEUE && eventMgr) { + eventMgr->dequeued(msg); } } -namespace -{ - const std::string qpidMaxSize("qpid.max_size"); - const std::string qpidMaxCount("qpid.max_count"); - const std::string qpidNoLocal("no-local"); - const std::string qpidTraceIdentity("qpid.trace.id"); - const std::string qpidTraceExclude("qpid.trace.exclude"); -} - void Queue::create(const FieldTable& _settings) { settings = _settings; @@ -545,26 +823,56 @@ void Queue::create(const FieldTable& _settings) configure(_settings); } -void Queue::configure(const FieldTable& _settings) +void Queue::configure(const FieldTable& _settings, bool recovering) { - std::auto_ptr<QueuePolicy> _policy(new QueuePolicy(_settings)); - if (_policy->getMaxCount() || _policy->getMaxSize()) { - setPolicy(_policy); + + eventMode = _settings.getAsInt(qpidQueueEventGeneration); + + if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK && + (!store || NullMessageStore::isNullStore(store) || (eventMode && eventMgr && !eventMgr->isSync()) )) { + if ( NullMessageStore::isNullStore(store)) { + QPID_LOG(warning, "Flow to disk not valid for non-persisted queue:" << getName()); + } else if (eventMgr && !eventMgr->isSync() ) { + QPID_LOG(warning, "Flow to disk not valid with async Queue Events:" << getName()); + } + FieldTable copy(_settings); + copy.erase(QueuePolicy::typeKey); + setPolicy(QueuePolicy::createQueuePolicy(getName(), copy)); + } else { + setPolicy(QueuePolicy::createQueuePolicy(getName(), _settings)); } //set this regardless of owner to allow use of no-local with exclusive consumers also noLocal = _settings.get(qpidNoLocal); - QPID_LOG(debug, "Configured queue with no-local=" << noLocal); + QPID_LOG(debug, "Configured queue " << getName() << " with no-local=" << noLocal); + + lastValueQueue= _settings.get(qpidLastValueQueue); + if (lastValueQueue) QPID_LOG(debug, "Configured queue as Last Value Queue for: " << getName()); - traceId = _settings.getString(qpidTraceIdentity); - std::string excludeList = _settings.getString(qpidTraceExclude); + lastValueQueueNoBrowse = _settings.get(qpidLastValueQueueNoBrowse); + if (lastValueQueueNoBrowse){ + QPID_LOG(debug, "Configured queue as Last Value Queue No Browse for: " << getName()); + lastValueQueue = lastValueQueueNoBrowse; + } + + persistLastNode= _settings.get(qpidPersistLastNode); + if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node for: " << getName()); + + traceId = _settings.getAsString(qpidTraceIdentity); + std::string excludeList = _settings.getAsString(qpidTraceExclude); if (excludeList.size()) { split(traceExclude, excludeList, ", "); } QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId << "' and qpid.trace.exclude='"<< excludeList << "' i.e. " << traceExclude.size() << " elements"); + FieldTable::ValuePtr p =_settings.get(qpidInsertSequenceNumbers); + if (p && p->convertsTo<std::string>()) insertSequenceNumbers(p->get<std::string>()); + if (mgmtObject != 0) mgmtObject->set_arguments (_settings); + + if ( isDurable() && ! getPersistenceId() && ! recovering ) + store->create(*this, _settings); } void Queue::destroy() @@ -572,7 +880,7 @@ void Queue::destroy() if (alternateExchange.get()) { Mutex::ScopedLock locker(messageLock); while(!messages.empty()){ - DeliverableMessage msg(messages.front().payload); + DeliverableMessage msg(getFront().payload); alternateExchange->route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); popAndDequeue(); @@ -617,8 +925,8 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const { if (mgmtObject != 0 && persistenceId == 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); - agent->addObject (mgmtObject, _persistenceId, 3); + ManagementAgent* agent = broker->getManagementAgent(); + agent->addObject (mgmtObject, 0x3000000000000000LL + _persistenceId); if (externalQueueStore) { ManagementObject* childObj = externalQueueStore->GetManagementObject(); @@ -629,24 +937,40 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const persistenceId = _persistenceId; } -void Queue::encode(framing::Buffer& buffer) const +void Queue::encode(Buffer& buffer) const { buffer.putShortString(name); buffer.put(settings); + if (policy.get()) { + buffer.put(*policy); + } + buffer.putShortString(alternateExchange.get() ? alternateExchange->getName() : std::string("")); } uint32_t Queue::encodedSize() const { - return name.size() + 1/*short string size octet*/ + settings.size(); + return name.size() + 1/*short string size octet*/ + + (alternateExchange.get() ? alternateExchange->getName().size() : 0) + 1 /* short string */ + + settings.encodedSize() + + (policy.get() ? (*policy).encodedSize() : 0); } -Queue::shared_ptr Queue::decode(QueueRegistry& queues, framing::Buffer& buffer) +Queue::shared_ptr Queue::decode ( QueueRegistry& queues, Buffer& buffer, bool recovering ) { string name; buffer.getShortString(name); std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true); buffer.get(result.first->settings); - result.first->configure(result.first->settings); + result.first->configure(result.first->settings, recovering ); + if (result.first->policy.get() && buffer.available() >= result.first->policy->encodedSize()) { + buffer.get ( *(result.first->policy) ); + } + if (buffer.available()) { + string altExch; + buffer.getShortString(altExch); + result.first->alternateExchangeName.assign(altExch); + } + return result.first; } @@ -654,6 +978,12 @@ Queue::shared_ptr Queue::decode(QueueRegistry& queues, framing::Buffer& buffer) void Queue::setAlternateExchange(boost::shared_ptr<Exchange> exchange) { alternateExchange = exchange; + if (mgmtObject) { + if (exchange.get() != 0) + mgmtObject->set_altExchange(exchange->GetManagementObject()->getObjectId()); + else + mgmtObject->clr_altExchange(); + } } boost::shared_ptr<Exchange> Queue::getAlternateExchange() @@ -721,8 +1051,7 @@ ManagementObject* Queue::GetManagementObject (void) const return (ManagementObject*) mgmtObject; } -Manageable::status_t Queue::ManagementMethod (uint32_t methodId, - Args& args) +Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, string&) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; @@ -730,8 +1059,8 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, switch (methodId) { - case management::Queue::METHOD_PURGE : - management::ArgsQueuePurge iargs = dynamic_cast<const management::ArgsQueuePurge&>(args); + case _qmf::Queue::METHOD_PURGE : + _qmf::ArgsQueuePurge& iargs = (_qmf::ArgsQueuePurge&) args; purge (iargs.i_request); status = Manageable::STATUS_OK; break; @@ -739,3 +1068,63 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, return status; } + +void Queue::setPosition(SequenceNumber n) { + Mutex::ScopedLock locker(messageLock); + sequence = n; +} + +SequenceNumber Queue::getPosition() { + return sequence; +} + +int Queue::getEventMode() { return eventMode; } + +void Queue::setQueueEventManager(QueueEvents& mgr) +{ + eventMgr = &mgr; +} + +void Queue::recoveryComplete(ExchangeRegistry& exchanges) +{ + // set the alternate exchange + if (!alternateExchangeName.empty()) { + try { + Exchange::shared_ptr ae = exchanges.get(alternateExchangeName); + setAlternateExchange(ae); + } catch (const NotFoundException&) { + QPID_LOG(warning, "Could not set alternate exchange \"" << alternateExchangeName << "\" on queue \"" << name << "\": exchange does not exist."); + } + } + //process any pending dequeues + for_each(pendingDequeues.begin(), pendingDequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1)); + pendingDequeues.clear(); +} + +void Queue::insertSequenceNumbers(const std::string& key) +{ + seqNoKey = key; + insertSeqNo = !seqNoKey.empty(); + QPID_LOG(debug, "Inserting sequence numbers as " << key); +} + +void Queue::enqueued(const QueuedMessage& m) +{ + if (m.payload) { + if (policy.get()) { + policy->recoverEnqueued(m.payload); + policy->enqueued(m); + } + mgntEnqStats(m.payload); + enqueue ( 0, m.payload, true ); + } else { + QPID_LOG(warning, "Queue informed of enqueued message that has no payload"); + } +} + +bool Queue::isEnqueued(const QueuedMessage& msg) +{ + return !policy.get() || policy->isEnqueued(msg); +} + +QueueListeners& Queue::getListeners() { return listeners; } diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h index 8b8ba8278f..5b177f1cf2 100644 --- a/cpp/src/qpid/broker/Queue.h +++ b/cpp/src/qpid/broker/Queue.h @@ -21,31 +21,38 @@ * under the License. * */ -#include "OwnershipToken.h" -#include "Consumer.h" -#include "Message.h" -#include "PersistableQueue.h" -#include "QueuePolicy.h" -#include "QueueBindings.h" + +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/OwnershipToken.h" +#include "qpid/broker/Consumer.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/PersistableQueue.h" +#include "qpid/broker/QueuePolicy.h" +#include "qpid/broker/QueueBindings.h" +#include "qpid/broker/QueueListeners.h" +#include "qpid/broker/RateTracker.h" #include "qpid/framing/FieldTable.h" #include "qpid/sys/Monitor.h" #include "qpid/management/Manageable.h" -#include "qpid/management/Queue.h" +#include "qmf/org/apache/qpid/broker/Queue.h" #include "qpid/framing/amqp_types.h" -#include <vector> -#include <memory> -#include <deque> - #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/intrusive_ptr.hpp> +#include <list> +#include <vector> +#include <memory> +#include <deque> +#include <algorithm> + namespace qpid { namespace broker { class Broker; class MessageStore; + class QueueEvents; class QueueRegistry; class TransactionContext; class Exchange; @@ -60,8 +67,10 @@ namespace qpid { */ class Queue : public boost::enable_shared_from_this<Queue>, public PersistableQueue, public management::Manageable { - typedef qpid::InlineVector<Consumer*, 5> Listeners; + typedef std::deque<QueuedMessage> Messages; + typedef std::map<string,boost::intrusive_ptr<Message> > LVQ; + enum ConsumeCode {NO_MESSAGES=0, CANT_CONSUME=1, CONSUMED=2}; const string name; const bool autodelete; @@ -70,10 +79,16 @@ namespace qpid { uint32_t consumerCount; OwnershipToken* exclusive; bool noLocal; + bool lastValueQueue; + bool lastValueQueueNoBrowse; + bool persistLastNode; + bool inLastNodeFailure; std::string traceId; std::vector<std::string> traceExclude; - Listeners listeners; + QueueListeners listeners; Messages messages; + Messages pendingDequeues;//used to avoid dequeuing during recovery + LVQ lvq; mutable qpid::sys::Mutex consumerLock; mutable qpid::sys::Mutex messageLock; mutable qpid::sys::Mutex ownershipLock; @@ -82,25 +97,59 @@ namespace qpid { std::auto_ptr<QueuePolicy> policy; bool policyExceeded; QueueBindings bindings; + std::string alternateExchangeName; boost::shared_ptr<Exchange> alternateExchange; framing::SequenceNumber sequence; - management::Queue* mgmtObject; - - void push(boost::intrusive_ptr<Message>& msg); + qmf::org::apache::qpid::broker::Queue* mgmtObject; + RateTracker dequeueTracker; + int eventMode; + QueueEvents* eventMgr; + bool insertSeqNo; + std::string seqNoKey; + Broker* broker; + + void push(boost::intrusive_ptr<Message>& msg, bool isRecovery=false); void setPolicy(std::auto_ptr<QueuePolicy> policy); - bool seek(QueuedMessage& msg, Consumer& position); - bool getNextMessage(QueuedMessage& msg, Consumer& c); - bool consumeNextMessage(QueuedMessage& msg, Consumer& c); - bool browseNextMessage(QueuedMessage& msg, Consumer& c); + bool seek(QueuedMessage& msg, Consumer::shared_ptr position); + bool getNextMessage(QueuedMessage& msg, Consumer::shared_ptr c); + ConsumeCode consumeNextMessage(QueuedMessage& msg, Consumer::shared_ptr c); + bool browseNextMessage(QueuedMessage& msg, Consumer::shared_ptr c); + void notifyListener(); - void notify(); - void removeListener(Consumer&); - void addListener(Consumer&); + void removeListener(Consumer::shared_ptr); bool isExcluded(boost::intrusive_ptr<Message>& msg); - void dequeued(boost::intrusive_ptr<Message>& msg); + void dequeued(const QueuedMessage& msg); void popAndDequeue(); + QueuedMessage getFront(); + QueuedMessage& checkLvqReplace(QueuedMessage& msg); + void clearLVQIndex(const QueuedMessage& msg); + + inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg) + { + if (mgmtObject != 0) { + mgmtObject->inc_msgTotalEnqueues (); + mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + if (msg->isPersistent ()) { + mgmtObject->inc_msgPersistEnqueues (); + mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); + } + } + } + inline void mgntDeqStats(const boost::intrusive_ptr<Message>& msg) + { + if (mgmtObject != 0){ + mgmtObject->inc_msgTotalDequeues (); + mgmtObject->inc_byteTotalDequeues (msg->contentSize()); + if (msg->isPersistent ()){ + mgmtObject->inc_msgPersistDequeues (); + mgmtObject->inc_bytePersistDequeues (msg->contentSize()); + } + } + } + + Messages::iterator findAt(framing::SequenceNumber pos); public: @@ -109,58 +158,73 @@ namespace qpid { typedef std::vector<shared_ptr> vector; - Queue(const string& name, bool autodelete = false, - MessageStore* const store = 0, - const OwnershipToken* const owner = 0, - management::Manageable* parent = 0); - ~Queue(); + QPID_BROKER_EXTERN Queue(const string& name, + bool autodelete = false, + MessageStore* const store = 0, + const OwnershipToken* const owner = 0, + management::Manageable* parent = 0, + Broker* broker = 0); + QPID_BROKER_EXTERN ~Queue(); - bool dispatch(Consumer&); + QPID_BROKER_EXTERN bool dispatch(Consumer::shared_ptr); /** * Check whether there would be a message available for * dispatch to this consumer. If not, the consumer will be * notified of events that may have changed this * situation. */ - bool checkForMessages(Consumer&); + bool checkForMessages(Consumer::shared_ptr); void create(const qpid::framing::FieldTable& settings); - void configure(const qpid::framing::FieldTable& settings); + + // "recovering" means we are doing a MessageStore recovery. + QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings, + bool recovering = false); void destroy(); - void bound(const string& exchange, const string& key, const qpid::framing::FieldTable& args); - void unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref); + QPID_BROKER_EXTERN void bound(const string& exchange, + const string& key, + const qpid::framing::FieldTable& args); + QPID_BROKER_EXTERN void unbind(ExchangeRegistry& exchanges, + Queue::shared_ptr shared_ref); - bool acquire(const QueuedMessage& msg); + QPID_BROKER_EXTERN bool acquire(const QueuedMessage& msg); + QPID_BROKER_EXTERN bool acquireMessageAt(const qpid::framing::SequenceNumber& position, QueuedMessage& message); /** * Delivers a message to the queue. Will record it as * enqueued if persistent then process it. */ - void deliver(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void deliver(boost::intrusive_ptr<Message>& msg); /** * Dispatches the messages immediately to a consumer if * one is available or stores it for later if not. */ - void process(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void process(boost::intrusive_ptr<Message>& msg); /** * Returns a message to the in-memory queue (due to lack * of acknowledegement from a receiver). If a consumer is * available it will be dispatched immediately, else it * will be returned to the front of the queue. */ - void requeue(const QueuedMessage& msg); + QPID_BROKER_EXTERN void requeue(const QueuedMessage& msg); /** * Used during recovery to add stored messages back to the queue */ - void recover(boost::intrusive_ptr<Message>& msg); + QPID_BROKER_EXTERN void recover(boost::intrusive_ptr<Message>& msg); - void consume(Consumer& c, bool exclusive = false); - void cancel(Consumer& c); + QPID_BROKER_EXTERN void consume(Consumer::shared_ptr c, + 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 void purgeExpired(); - uint32_t getMessageCount() const; - uint32_t getConsumerCount() const; + //move qty # of messages to destination Queue destq + uint32_t move(const Queue::shared_ptr destq, uint32_t qty); + + QPID_BROKER_EXTERN uint32_t getMessageCount() const; + QPID_BROKER_EXTERN uint32_t getEnqueueCompleteMessageCount() const; + QPID_BROKER_EXTERN uint32_t getConsumerCount() const; inline const string& getName() const { return name; } bool isExclusiveOwner(const OwnershipToken* const o) const; void releaseExclusiveOwnership(); @@ -171,17 +235,50 @@ namespace qpid { inline const framing::FieldTable& getSettings() const { return settings; } inline bool isAutoDelete() const { return autodelete; } bool canAutoDelete() const; + const QueueBindings& getBindings() const { return bindings; } + + /** + * used to take messages from in memory and flush down to disk. + */ + QPID_BROKER_EXTERN void setLastNodeFailure(); + QPID_BROKER_EXTERN void clearLastNodeFailure(); - bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg); + bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg, bool suppressPolicyCheck = false); + void enqueueAborted(boost::intrusive_ptr<Message> msg); /** * dequeue from store (only done once messages is acknowledged) */ - bool dequeue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN bool dequeue(TransactionContext* ctxt, const QueuedMessage &msg); + /** + * Inform the queue that a previous transactional dequeue + * committed. + */ + void dequeueCommitted(const QueuedMessage& msg); + + /** + * Inform queue of messages that were enqueued, have since + * been acquired but not yet accepted or released (and + * thus are still logically on the queue) - used in + * clustered broker. + */ + void enqueued(const QueuedMessage& msg); /** + * Test whether the specified message (identified by its + * sequence/position), is still enqueued (note this + * doesn't mean it is available for delivery as it may + * have been delievered to a subscriber who has not yet + * accepted it). + */ + bool isEnqueued(const QueuedMessage& msg); + + /** * Gets the next available message */ - QueuedMessage get(); + QPID_BROKER_EXTERN QueuedMessage get(); + + /** Get the message at position pos */ + QPID_BROKER_EXTERN QueuedMessage find(framing::SequenceNumber pos) const; const QueuePolicy* getPolicy(); @@ -195,7 +292,8 @@ namespace qpid { void encode(framing::Buffer& buffer) const; uint32_t encodedSize() const; - static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer); + // "recovering" means we are doing a MessageStore recovery. + static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer, bool recovering = false ); static void tryAutoDelete(Broker& broker, Queue::shared_ptr); virtual void setExternalQueueStore(ExternalQueueStore* inst); @@ -203,7 +301,51 @@ namespace qpid { // Manageable entry points management::ManagementObject* GetManagementObject (void) const; management::Manageable::status_t - ManagementMethod (uint32_t methodId, management::Args& args); + ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); + + /** Apply f to each Message on the queue. */ + template <class F> void eachMessage(F f) { + sys::Mutex::ScopedLock l(messageLock); + if (lastValueQueue) { + for (Messages::iterator i = messages.begin(); i != messages.end(); ++i) { + f(checkLvqReplace(*i)); + } + } else { + std::for_each(messages.begin(), messages.end(), f); + } + } + + /** Apply f to each QueueBinding on the queue */ + template <class F> void eachBinding(F f) { + bindings.eachBinding(f); + } + + void popMsg(QueuedMessage& qmsg); + + /** Set the position sequence number for the next message on the queue. + * Must be >= the current sequence number. + * Used by cluster to replicate queues. + */ + QPID_BROKER_EXTERN void setPosition(framing::SequenceNumber pos); + /** return current position sequence number for the next message on the queue. + */ + QPID_BROKER_EXTERN framing::SequenceNumber getPosition(); + int getEventMode(); + void setQueueEventManager(QueueEvents&); + QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key); + /** + * Notify queue that recovery has completed. + */ + void recoveryComplete(ExchangeRegistry& exchanges); + + // For cluster update + QueueListeners& getListeners(); + + /** + * 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); }; } } diff --git a/cpp/src/qpid/broker/QueueBindings.cpp b/cpp/src/qpid/broker/QueueBindings.cpp index 95e529f47e..3f43a8ef68 100644 --- a/cpp/src/qpid/broker/QueueBindings.cpp +++ b/cpp/src/qpid/broker/QueueBindings.cpp @@ -18,8 +18,8 @@ * under the License. * */ -#include "QueueBindings.h" -#include "ExchangeRegistry.h" +#include "qpid/broker/QueueBindings.h" +#include "qpid/broker/ExchangeRegistry.h" #include "qpid/framing/reply_exceptions.h" using qpid::framing::FieldTable; @@ -29,7 +29,7 @@ using namespace qpid::broker; void QueueBindings::add(const string& exchange, const string& key, const FieldTable& args) { - bindings.push_back(new Binding(exchange, key, args)); + bindings.push_back(QueueBinding(exchange, key, args)); } void QueueBindings::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr queue) @@ -37,11 +37,10 @@ void QueueBindings::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr queue) for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) { try { exchanges.get(i->exchange)->unbind(queue, i->key, &(i->args)); - } catch (const NotFoundException&) { - } + } catch (const NotFoundException&) {} } } -QueueBindings::Binding::Binding(const string& _exchange, const string& _key, const FieldTable& _args) +QueueBinding::QueueBinding(const string& _exchange, const string& _key, const FieldTable& _args) : exchange(_exchange), key(_key), args(_args) {} diff --git a/cpp/src/qpid/broker/QueueBindings.h b/cpp/src/qpid/broker/QueueBindings.h index b9b0f7c15c..1b90ba5540 100644 --- a/cpp/src/qpid/broker/QueueBindings.h +++ b/cpp/src/qpid/broker/QueueBindings.h @@ -24,32 +24,38 @@ #include "qpid/framing/FieldTable.h" #include <boost/ptr_container/ptr_list.hpp> #include <boost/shared_ptr.hpp> +#include <algorithm> namespace qpid { namespace broker { class ExchangeRegistry; class Queue; + +struct QueueBinding{ + std::string exchange; + std::string key; + qpid::framing::FieldTable args; + QueueBinding(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); +}; + class QueueBindings { - struct Binding{ - const std::string exchange; - const std::string key; - const qpid::framing::FieldTable args; - Binding(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); - }; - - typedef boost::ptr_list<Binding> Bindings; - Bindings bindings; + public: -public: + /** Apply f to each QueueBinding. */ + template <class F> void eachBinding(F f) const { std::for_each(bindings.begin(), bindings.end(), f); } + void add(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); void unbind(ExchangeRegistry& exchanges, boost::shared_ptr<Queue> queue); + + private: + typedef std::vector<QueueBinding> Bindings; + Bindings bindings; }; -} -} +}} // namespace qpid::broker #endif diff --git a/cpp/src/qpid/broker/QueueCleaner.cpp b/cpp/src/qpid/broker/QueueCleaner.cpp new file mode 100644 index 0000000000..c80fe89035 --- /dev/null +++ b/cpp/src/qpid/broker/QueueCleaner.cpp @@ -0,0 +1,57 @@ +/* + * + * 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/QueueCleaner.h" + +#include "qpid/broker/Broker.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer& t) : queues(q), timer(t) {} + +QueueCleaner::~QueueCleaner() +{ + if (task) task->cancel(); +} + +void QueueCleaner::start(qpid::sys::Duration p) +{ + task = new Task(*this, p); + timer.add(task); +} + +QueueCleaner::Task::Task(QueueCleaner& p, qpid::sys::Duration d) : sys::TimerTask(d), parent(p) {} + +void QueueCleaner::Task::fire() +{ + parent.fired(); +} + +void QueueCleaner::fired() +{ + queues.eachQueue(boost::bind(&Queue::purgeExpired, _1)); + task->setupNextFire(); + timer.add(task); +} + + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueCleaner.h b/cpp/src/qpid/broker/QueueCleaner.h new file mode 100644 index 0000000000..11c2d180ac --- /dev/null +++ b/cpp/src/qpid/broker/QueueCleaner.h @@ -0,0 +1,59 @@ +#ifndef QPID_BROKER_QUEUECLEANER_H +#define QPID_BROKER_QUEUECLEANER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/sys/Timer.h" + +namespace qpid { +namespace broker { + +class QueueRegistry; +/** + * TimerTask to purge expired messages from queues + */ +class QueueCleaner +{ + public: + QPID_BROKER_EXTERN QueueCleaner(QueueRegistry& queues, sys::Timer& timer); + QPID_BROKER_EXTERN ~QueueCleaner(); + QPID_BROKER_EXTERN void start(qpid::sys::Duration period); + private: + class Task : public sys::TimerTask + { + public: + Task(QueueCleaner& parent, qpid::sys::Duration duration); + void fire(); + private: + QueueCleaner& parent; + }; + + boost::intrusive_ptr<sys::TimerTask> task; + QueueRegistry& queues; + sys::Timer& timer; + + void fired(); +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUECLEANER_H*/ diff --git a/cpp/src/qpid/broker/QueueEvents.cpp b/cpp/src/qpid/broker/QueueEvents.cpp new file mode 100644 index 0000000000..bba054b0b8 --- /dev/null +++ b/cpp/src/qpid/broker/QueueEvents.cpp @@ -0,0 +1,122 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/QueueEvents.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { + +QueueEvents::QueueEvents(const boost::shared_ptr<sys::Poller>& poller, bool isSync) : + eventQueue(boost::bind(&QueueEvents::handle, this, _1), poller), enabled(true), sync(isSync) +{ + if (!sync) eventQueue.start(); +} + +QueueEvents::~QueueEvents() +{ + if (!sync) eventQueue.stop(); +} + +void QueueEvents::enqueued(const QueuedMessage& m) +{ + if (enabled) { + Event enq(ENQUEUE, m); + if (sync) { + for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) + j->second(enq); + } else { + eventQueue.push(enq); + } + } +} + +void QueueEvents::dequeued(const QueuedMessage& m) +{ + if (enabled) { + Event deq(DEQUEUE, m); + if (sync) { + for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) + j->second(deq); + } else { + eventQueue.push(Event(DEQUEUE, m)); + } + } +} + +void QueueEvents::registerListener(const std::string& id, const EventListener& listener) +{ + qpid::sys::Mutex::ScopedLock l(lock); + if (listeners.find(id) == listeners.end()) { + listeners[id] = listener; + } else { + throw Exception(QPID_MSG("Event listener already registered for '" << id << "'")); + } +} + +void QueueEvents::unregisterListener(const std::string& id) +{ + qpid::sys::Mutex::ScopedLock l(lock); + if (listeners.find(id) == listeners.end()) { + throw Exception(QPID_MSG("No event listener registered for '" << id << "'")); + } else { + listeners.erase(id); + } +} + +QueueEvents::EventQueue::Batch::const_iterator +QueueEvents::handle(const EventQueue::Batch& events) { + qpid::sys::Mutex::ScopedLock l(lock); + for (EventQueue::Batch::const_iterator i = events.begin(); i != events.end(); ++i) { + for (Listeners::iterator j = listeners.begin(); j != listeners.end(); j++) { + j->second(*i); + } + } + return events.end(); +} + +void QueueEvents::shutdown() +{ + if (!sync && !eventQueue.empty() && !listeners.empty()) eventQueue.shutdown(); +} + +void QueueEvents::enable() +{ + enabled = true; + QPID_LOG(debug, "Queue events enabled"); +} + +void QueueEvents::disable() +{ + enabled = false; + QPID_LOG(debug, "Queue events disabled"); +} + +bool QueueEvents::isSync() +{ + return sync; +} + + +QueueEvents::Event::Event(EventType t, const QueuedMessage& m) : type(t), msg(m) {} + + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueEvents.h b/cpp/src/qpid/broker/QueueEvents.h new file mode 100644 index 0000000000..c42752133e --- /dev/null +++ b/cpp/src/qpid/broker/QueueEvents.h @@ -0,0 +1,84 @@ +#ifndef QPID_BROKER_QUEUEEVENTS_H +#define QPID_BROKER_QUEUEEVENTS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/QueuedMessage.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/PollableQueue.h" +#include <map> +#include <string> +#include <boost/function.hpp> + +namespace qpid { +namespace broker { + +/** + * Event manager for queue events. Allows queues to indicate when + * events have occured; allows listeners to register for notification + * of this. The notification happens asynchronously, in a separate + * thread. + */ +class QueueEvents +{ + public: + enum EventType {ENQUEUE, DEQUEUE}; + + struct Event + { + EventType type; + QueuedMessage msg; + + QPID_BROKER_EXTERN Event(EventType, const QueuedMessage&); + }; + + typedef boost::function<void (Event)> EventListener; + + QPID_BROKER_EXTERN QueueEvents(const boost::shared_ptr<sys::Poller>& poller, bool isSync = false); + QPID_BROKER_EXTERN ~QueueEvents(); + QPID_BROKER_EXTERN void enqueued(const QueuedMessage&); + QPID_BROKER_EXTERN void dequeued(const QueuedMessage&); + QPID_BROKER_EXTERN void registerListener(const std::string& id, + const EventListener&); + QPID_BROKER_EXTERN void unregisterListener(const std::string& id); + void enable(); + void disable(); + //process all outstanding events + QPID_BROKER_EXTERN void shutdown(); + QPID_BROKER_EXTERN bool isSync(); + private: + typedef qpid::sys::PollableQueue<Event> EventQueue; + typedef std::map<std::string, EventListener> Listeners; + + EventQueue eventQueue; + Listeners listeners; + volatile bool enabled; + qpid::sys::Mutex lock;//protect listeners from concurrent access + bool sync; + + EventQueue::Batch::const_iterator handle(const EventQueue::Batch& e); + +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUEEVENTS_H*/ diff --git a/cpp/src/qpid/broker/QueueListeners.cpp b/cpp/src/qpid/broker/QueueListeners.cpp new file mode 100644 index 0000000000..951de2184a --- /dev/null +++ b/cpp/src/qpid/broker/QueueListeners.cpp @@ -0,0 +1,81 @@ +/* + * + * 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/QueueListeners.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +void QueueListeners::addListener(Consumer::shared_ptr c) +{ + if (c->preAcquires()) { + add(consumers, c); + } else { + add(browsers, c); + } +} + +void QueueListeners::removeListener(Consumer::shared_ptr c) +{ + if (c->preAcquires()) { + remove(consumers, c); + } else { + remove(browsers, c); + } +} + +void QueueListeners::populate(NotificationSet& set) +{ + if (consumers.size()) { + set.consumer = consumers.front(); + consumers.erase(consumers.begin()); + } else { + // Don't swap the vectors, hang on to the memory allocated. + set.browsers = browsers; + browsers.clear(); + } +} + +void QueueListeners::add(Listeners& listeners, Consumer::shared_ptr c) +{ + Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c); + if (i == listeners.end()) listeners.push_back(c); +} + +void QueueListeners::remove(Listeners& listeners, Consumer::shared_ptr c) +{ + Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c); + if (i != listeners.end()) listeners.erase(i); +} + +void QueueListeners::NotificationSet::notify() +{ + if (consumer) consumer->notify(); + else std::for_each(browsers.begin(), browsers.end(), boost::mem_fn(&Consumer::notify)); +} + +bool QueueListeners::contains(Consumer::shared_ptr c) const { + return + std::find(browsers.begin(), browsers.end(), c) != browsers.end() || + std::find(consumers.begin(), consumers.end(), c) != consumers.end(); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueListeners.h b/cpp/src/qpid/broker/QueueListeners.h new file mode 100644 index 0000000000..51ef58eb06 --- /dev/null +++ b/cpp/src/qpid/broker/QueueListeners.h @@ -0,0 +1,75 @@ +#ifndef QPID_BROKER_QUEUELISTENERS_H +#define QPID_BROKER_QUEUELISTENERS_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/Consumer.h" +#include <vector> + +namespace qpid { +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 + * called outside of any lock that protects the QueueListeners + * instance from concurrent access. + */ +class QueueListeners +{ + public: + typedef std::vector<Consumer::shared_ptr> Listeners; + + class NotificationSet + { + public: + void notify(); + private: + Listeners browsers; + Consumer::shared_ptr consumer; + friend class QueueListeners; + }; + + void addListener(Consumer::shared_ptr); + void removeListener(Consumer::shared_ptr); + void populate(NotificationSet&); + bool contains(Consumer::shared_ptr c) const; + + template <class F> void eachListener(F f) { + std::for_each(browsers.begin(), browsers.end(), f); + std::for_each(consumers.begin(), consumers.end(), f); + } + + private: + Listeners consumers; + Listeners browsers; + + void add(Listeners&, Consumer::shared_ptr); + void remove(Listeners&, Consumer::shared_ptr); + +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_QUEUELISTENERS_H*/ diff --git a/cpp/src/qpid/broker/QueuePolicy.cpp b/cpp/src/qpid/broker/QueuePolicy.cpp index 08838aac79..a8aa674c53 100644 --- a/cpp/src/qpid/broker/QueuePolicy.cpp +++ b/cpp/src/qpid/broker/QueuePolicy.cpp @@ -18,40 +18,99 @@ * under the License. * */ -#include "QueuePolicy.h" +#include "qpid/broker/QueuePolicy.h" +#include "qpid/broker/Queue.h" +#include "qpid/Exception.h" #include "qpid/framing/FieldValue.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" using namespace qpid::broker; using namespace qpid::framing; -QueuePolicy::QueuePolicy(uint32_t _maxCount, uint64_t _maxSize) : - maxCount(_maxCount), maxSize(_maxSize), count(0), size(0) {} - -QueuePolicy::QueuePolicy(const FieldTable& settings) : - maxCount(getInt(settings, maxCountKey, 0)), - maxSize(getInt(settings, maxSizeKey, defaultMaxSize)), count(0), size(0) {} +QueuePolicy::QueuePolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : + maxCount(_maxCount), maxSize(_maxSize), type(_type), count(0), size(0), policyExceeded(false), name(_name) {} void QueuePolicy::enqueued(uint64_t _size) { - if (maxCount) count++; + if (maxCount) ++count; if (maxSize) size += _size; } void QueuePolicy::dequeued(uint64_t _size) { - if (maxCount) count--; - if (maxSize) size -= _size; + if (maxCount) { + if (count > 0) { + --count; + } else { + throw Exception(QPID_MSG("Attempted count underflow on dequeue(" << _size << "): " << *this)); + } + } + if (maxSize) { + if (_size > size) { + throw Exception(QPID_MSG("Attempted size underflow on dequeue(" << _size << "): " << *this)); + } else { + size -= _size; + } + } +} + +bool QueuePolicy::checkLimit(boost::intrusive_ptr<Message> m) +{ + bool sizeExceeded = maxSize && (size + m->contentSize()) > maxSize; + bool countExceeded = maxCount && (count + 1) > maxCount; + bool exceeded = sizeExceeded || countExceeded; + if (exceeded) { + if (!policyExceeded) { + policyExceeded = true; + if (sizeExceeded) QPID_LOG(info, "Queue cumulative message size exceeded policy for " << name); + if (countExceeded) QPID_LOG(info, "Queue message count exceeded policy for " << name); + } + } else { + if (policyExceeded) { + policyExceeded = false; + QPID_LOG(info, "Queue cumulative message size and message count within policy for " << name); + } + } + return !exceeded; +} + +void QueuePolicy::tryEnqueue(boost::intrusive_ptr<Message> m) +{ + if (checkLimit(m)) { + enqueued(m->contentSize()); + } else { + throw ResourceLimitExceededException(QPID_MSG("Policy exceeded on " << name << ", policy: " << *this)); + } +} + +void QueuePolicy::recoverEnqueued(boost::intrusive_ptr<Message> m) +{ + enqueued(m->contentSize()); } -bool QueuePolicy::limitExceeded() +void QueuePolicy::enqueueAborted(boost::intrusive_ptr<Message> m) { - return (maxSize && size > maxSize) || (maxCount && count > maxCount); + dequeued(m->contentSize()); +} + +void QueuePolicy::enqueued(const QueuedMessage&) {} + +void QueuePolicy::dequeued(const QueuedMessage& m) +{ + dequeued(m.payload->contentSize()); +} + +bool QueuePolicy::isEnqueued(const QueuedMessage&) +{ + return true; } void QueuePolicy::update(FieldTable& settings) { if (maxCount) settings.setInt(maxCountKey, maxCount); - if (maxSize) settings.setInt(maxSizeKey, maxSize); + if (maxSize) settings.setInt(maxSizeKey, maxSize); + settings.setString(typeKey, type); } @@ -62,27 +121,195 @@ int QueuePolicy::getInt(const FieldTable& settings, const std::string& key, int else return defaultValue; } +std::string QueuePolicy::getType(const FieldTable& settings) +{ + FieldTable::ValuePtr v = settings.get(typeKey); + if (v && v->convertsTo<std::string>()) { + std::string t = v->get<std::string>(); + std::transform(t.begin(), t.end(), t.begin(), tolower); + if (t == REJECT || t == FLOW_TO_DISK || t == RING || t == RING_STRICT) return t; + } + return REJECT; +} + void QueuePolicy::setDefaultMaxSize(uint64_t s) { defaultMaxSize = s; } +void QueuePolicy::getPendingDequeues(Messages&) {} + + + + +void QueuePolicy::encode(Buffer& buffer) const +{ + buffer.putLong(maxCount); + buffer.putLongLong(maxSize); + buffer.putLong(count); + buffer.putLongLong(size); +} + +void QueuePolicy::decode ( Buffer& buffer ) +{ + maxCount = buffer.getLong(); + maxSize = buffer.getLongLong(); + count = buffer.getLong(); + size = buffer.getLongLong(); +} + + +uint32_t QueuePolicy::encodedSize() const { + return sizeof(uint32_t) + // maxCount + sizeof(uint64_t) + // maxSize + sizeof(uint32_t) + // count + sizeof(uint64_t); // size +} + + + const std::string QueuePolicy::maxCountKey("qpid.max_count"); const std::string QueuePolicy::maxSizeKey("qpid.max_size"); +const std::string QueuePolicy::typeKey("qpid.policy_type"); +const std::string QueuePolicy::REJECT("reject"); +const std::string QueuePolicy::FLOW_TO_DISK("flow_to_disk"); +const std::string QueuePolicy::RING("ring"); +const std::string QueuePolicy::RING_STRICT("ring_strict"); uint64_t QueuePolicy::defaultMaxSize(0); +FlowToDiskPolicy::FlowToDiskPolicy(const std::string& _name, uint32_t _maxCount, uint64_t _maxSize) : + QueuePolicy(_name, _maxCount, _maxSize, FLOW_TO_DISK) {} + +bool FlowToDiskPolicy::checkLimit(boost::intrusive_ptr<Message> m) +{ + if (!QueuePolicy::checkLimit(m)) m->requestContentRelease(); + return true; +} + +RingQueuePolicy::RingQueuePolicy(const std::string& _name, + uint32_t _maxCount, uint64_t _maxSize, const std::string& _type) : + QueuePolicy(_name, _maxCount, _maxSize, _type), strict(_type == RING_STRICT) {} + +bool before(const QueuedMessage& a, const QueuedMessage& b) +{ + return a.position < b.position; +} + +void RingQueuePolicy::enqueued(const QueuedMessage& m) +{ + //need to insert in correct location based on position + queue.insert(lower_bound(queue.begin(), queue.end(), m, before), m); +} + +void RingQueuePolicy::dequeued(const QueuedMessage& m) +{ + //find and remove m from queue + if (find(m, pendingDequeues, true) || find(m, queue, true)) { + //now update count and size + QueuePolicy::dequeued(m); + } +} + +bool RingQueuePolicy::isEnqueued(const QueuedMessage& m) +{ + //for non-strict ring policy, a message can be replaced (and + //therefore dequeued) before it is accepted or released by + //subscriber; need to detect this + return find(m, pendingDequeues, false) || find(m, queue, false); +} + +bool RingQueuePolicy::checkLimit(boost::intrusive_ptr<Message> m) +{ + if (QueuePolicy::checkLimit(m)) return true;//if haven't hit limit, ok to accept + + QueuedMessage oldest; + if (queue.empty()) { + QPID_LOG(debug, "Message too large for ring queue " << name + << " [" << *this << "] " + << ": message size = " << m->contentSize() << " bytes"); + return false; + } + oldest = queue.front(); + if (oldest.queue->acquire(oldest) || !strict) { + queue.pop_front(); + pendingDequeues.push_back(oldest); + QPID_LOG(debug, "Ring policy triggered in " << name + << ": removed message " << oldest.position << " to make way for new message"); + return true; + } else { + QPID_LOG(debug, "Ring policy could not be triggered in " << name + << ": oldest message (seq-no=" << oldest.position << ") has been delivered but not yet acknowledged or requeued"); + //in strict mode, if oldest message has been delivered (hence + //cannot be acquired) but not yet acked, it should not be + //removed and the attempted enqueue should fail + return false; + } +} + +void RingQueuePolicy::getPendingDequeues(Messages& result) +{ + result = pendingDequeues; +} + +bool RingQueuePolicy::find(const QueuedMessage& m, Messages& q, bool remove) +{ + for (Messages::iterator i = q.begin(); i != q.end(); i++) { + if (i->payload == m.payload) { + if (remove) q.erase(i); + return true; + } + } + return false; +} + +std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type) +{ + return createQueuePolicy("<unspecified>", maxCount, maxSize, type); +} + +std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const qpid::framing::FieldTable& settings) +{ + return createQueuePolicy("<unspecified>", settings); +} + +std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings) +{ + uint32_t maxCount = getInt(settings, maxCountKey, 0); + uint32_t maxSize = getInt(settings, maxSizeKey, defaultMaxSize); + if (maxCount || maxSize) { + return createQueuePolicy(name, maxCount, maxSize, getType(settings)); + } else { + return std::auto_ptr<QueuePolicy>(); + } +} + +std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name, + uint32_t maxCount, uint64_t maxSize, const std::string& type) +{ + if (type == RING || type == RING_STRICT) { + return std::auto_ptr<QueuePolicy>(new RingQueuePolicy(name, maxCount, maxSize, type)); + } else if (type == FLOW_TO_DISK) { + return std::auto_ptr<QueuePolicy>(new FlowToDiskPolicy(name, maxCount, maxSize)); + } else { + return std::auto_ptr<QueuePolicy>(new QueuePolicy(name, maxCount, maxSize, type)); + } + +} + namespace qpid { namespace broker { std::ostream& operator<<(std::ostream& out, const QueuePolicy& p) { if (p.maxSize) out << "size: max=" << p.maxSize << ", current=" << p.size; - else out << "size unlimited, current=" << p.size; + else out << "size: unlimited"; out << "; "; if (p.maxCount) out << "count: max=" << p.maxCount << ", current=" << p.count; - else out << "count unlimited, current=" << p.count; + else out << "count: unlimited"; + out << "; type=" << p.type; return out; } } } + diff --git a/cpp/src/qpid/broker/QueuePolicy.h b/cpp/src/qpid/broker/QueuePolicy.h index 4511a63b64..b2937e94c7 100644 --- a/cpp/src/qpid/broker/QueuePolicy.h +++ b/cpp/src/qpid/broker/QueuePolicy.h @@ -21,40 +21,100 @@ #ifndef _QueuePolicy_ #define _QueuePolicy_ +#include <deque> #include <iostream> +#include <memory> +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/QueuedMessage.h" #include "qpid/framing/FieldTable.h" +#include "qpid/sys/AtomicValue.h" +#include "qpid/sys/Mutex.h" namespace qpid { - namespace broker { - class QueuePolicy - { - static const std::string maxCountKey; - static const std::string maxSizeKey; - - static uint64_t defaultMaxSize; +namespace broker { + +class QueuePolicy +{ + static uint64_t defaultMaxSize; - const uint32_t maxCount; - const uint64_t maxSize; - uint32_t count; - uint64_t size; + uint32_t maxCount; + uint64_t maxSize; + const std::string type; + uint32_t count; + uint64_t size; + bool policyExceeded; - static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue); - - public: - QueuePolicy(uint32_t maxCount, uint64_t maxSize); - QueuePolicy(const qpid::framing::FieldTable& settings); - void enqueued(uint64_t size); - void dequeued(uint64_t size); - void update(qpid::framing::FieldTable& settings); - bool limitExceeded(); - uint32_t getMaxCount() const { return maxCount; } - uint64_t getMaxSize() const { return maxSize; } - - static void setDefaultMaxSize(uint64_t); - friend std::ostream& operator<<(std::ostream&, const QueuePolicy&); - }; - } -} + static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue); + + public: + typedef std::deque<QueuedMessage> Messages; + static QPID_BROKER_EXTERN const std::string maxCountKey; + static QPID_BROKER_EXTERN const std::string maxSizeKey; + static QPID_BROKER_EXTERN const std::string typeKey; + static QPID_BROKER_EXTERN const std::string REJECT; + static QPID_BROKER_EXTERN const std::string FLOW_TO_DISK; + static QPID_BROKER_EXTERN const std::string RING; + static QPID_BROKER_EXTERN const std::string RING_STRICT; + + virtual ~QueuePolicy() {} + QPID_BROKER_EXTERN void tryEnqueue(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN void recoverEnqueued(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN void enqueueAborted(boost::intrusive_ptr<Message> msg); + virtual void enqueued(const QueuedMessage&); + virtual void dequeued(const QueuedMessage&); + virtual bool isEnqueued(const QueuedMessage&); + QPID_BROKER_EXTERN void update(qpid::framing::FieldTable& settings); + uint32_t getMaxCount() const { return maxCount; } + uint64_t getMaxSize() const { return maxSize; } + void encode(framing::Buffer& buffer) const; + void decode ( framing::Buffer& buffer ); + uint32_t encodedSize() const; + virtual void getPendingDequeues(Messages& result); + + static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings); + static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(const qpid::framing::FieldTable& settings); + static QPID_BROKER_EXTERN std::auto_ptr<QueuePolicy> createQueuePolicy(uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + static std::string getType(const qpid::framing::FieldTable& settings); + static void setDefaultMaxSize(uint64_t); + friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, + const QueuePolicy&); + protected: + const std::string name; + + QueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = REJECT); + + virtual bool checkLimit(boost::intrusive_ptr<Message> msg); + void enqueued(uint64_t size); + void dequeued(uint64_t size); +}; + + +class FlowToDiskPolicy : public QueuePolicy +{ + public: + FlowToDiskPolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize); + bool checkLimit(boost::intrusive_ptr<Message> msg); +}; + +class RingQueuePolicy : public QueuePolicy +{ + public: + RingQueuePolicy(const std::string& name, uint32_t maxCount, uint64_t maxSize, const std::string& type = RING); + void enqueued(const QueuedMessage&); + void dequeued(const QueuedMessage&); + bool isEnqueued(const QueuedMessage&); + bool checkLimit(boost::intrusive_ptr<Message> msg); + void getPendingDequeues(Messages& result); + private: + Messages pendingDequeues; + Messages queue; + const bool strict; + + bool find(const QueuedMessage&, Messages&, bool remove); +}; + +}} #endif diff --git a/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp index 61bdb0ffde..4b1fa62709 100644 --- a/cpp/src/qpid/broker/QueueRegistry.cpp +++ b/cpp/src/qpid/broker/QueueRegistry.cpp @@ -18,7 +18,8 @@ * under the License. * */ -#include "QueueRegistry.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid/broker/QueueEvents.h" #include "qpid/log/Statement.h" #include <sstream> #include <assert.h> @@ -26,8 +27,8 @@ using namespace qpid::broker; using namespace qpid::sys; -QueueRegistry::QueueRegistry() : - counter(1), store(0), parent(0) {} +QueueRegistry::QueueRegistry(Broker* b) : + counter(1), store(0), events(0), parent(0), lastNode(false), broker(b) {} QueueRegistry::~QueueRegistry(){} @@ -41,8 +42,10 @@ QueueRegistry::declare(const string& declareName, bool durable, QueueMap::iterator i = queues.find(name); if (i == queues.end()) { - Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent)); + Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker)); queues[name] = queue; + if (lastNode) queue->setLastNodeFailure(); + if (events) queue->setQueueEventManager(*events); return std::pair<Queue::shared_ptr, bool>(queue, true); } else { @@ -84,10 +87,27 @@ string QueueRegistry::generateName(){ void QueueRegistry::setStore (MessageStore* _store) { - assert (store == 0 && _store != 0); store = _store; } MessageStore* QueueRegistry::getStore() const { return store; } + +void QueueRegistry::updateQueueClusterState(bool _lastNode) +{ + RWlock::ScopedRlock locker(lock); + for (QueueMap::iterator i = queues.begin(); i != queues.end(); i++) { + if (_lastNode){ + i->second->setLastNodeFailure(); + } else { + i->second->clearLastNodeFailure(); + } + } + lastNode = _lastNode; +} + +void QueueRegistry::setQueueEvents(QueueEvents* e) +{ + events = e; +} diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h index f7be1c551a..72a91dff24 100644 --- a/cpp/src/qpid/broker/QueueRegistry.h +++ b/cpp/src/qpid/broker/QueueRegistry.h @@ -21,14 +21,19 @@ #ifndef _QueueRegistry_ #define _QueueRegistry_ -#include <map> +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Queue.h" #include "qpid/sys/Mutex.h" -#include "Queue.h" #include "qpid/management/Manageable.h" +#include <boost/bind.hpp> +#include <algorithm> +#include <map> namespace qpid { namespace broker { +class QueueEvents; + /** * A registry of queues indexed by queue name. * @@ -36,10 +41,10 @@ namespace broker { * are deleted when and only when they are no longer in use. * */ -class QueueRegistry{ +class QueueRegistry { public: - QueueRegistry(); - ~QueueRegistry(); + QPID_BROKER_EXTERN QueueRegistry(Broker* b = 0); + QPID_BROKER_EXTERN ~QueueRegistry(); /** * Declare a queue. @@ -47,8 +52,11 @@ class QueueRegistry{ * @return The queue and a boolean flag which is true if the queue * was created by this declare call false if it already existed. */ - std::pair<Queue::shared_ptr, bool> declare(const string& name, bool durable = false, bool autodelete = false, - const OwnershipToken* owner = 0); + QPID_BROKER_EXTERN std::pair<Queue::shared_ptr, bool> declare + (const string& name, + bool durable = false, + bool autodelete = false, + const OwnershipToken* owner = 0); /** * Destroy the named queue. @@ -62,7 +70,7 @@ class QueueRegistry{ * subsequent calls to find or declare with the same name. * */ - void destroy (const string& name); + QPID_BROKER_EXTERN void destroy(const string& name); template <class Test> bool destroyIf(const string& name, Test test) { qpid::sys::RWlock::ScopedWlock locker(lock); @@ -77,13 +85,15 @@ class QueueRegistry{ /** * Find the named queue. Return 0 if not found. */ - Queue::shared_ptr find(const string& name); + QPID_BROKER_EXTERN Queue::shared_ptr find(const string& name); /** * Generate unique queue name. */ string generateName(); + void setQueueEvents(QueueEvents*); + /** * Set the store to use. May only be called once. */ @@ -98,22 +108,37 @@ class QueueRegistry{ * Register the manageable parent for declared queues */ void setParent (management::Manageable* _parent) { parent = _parent; } + + /** Call f for each queue in the registry. */ + template <class F> void eachQueue(F f) const { + qpid::sys::RWlock::ScopedRlock l(lock); + for (QueueMap::const_iterator i = queues.begin(); i != queues.end(); ++i) + f(i->second); + } + + /** + * Change queue mode when cluster size drops to 1 node, expands again + * in practice allows flow queue to disk when last name to be exectuted + */ + void updateQueueClusterState(bool lastNode); private: typedef std::map<string, Queue::shared_ptr> QueueMap; QueueMap queues; - qpid::sys::RWlock lock; + mutable qpid::sys::RWlock lock; int counter; MessageStore* store; + QueueEvents* events; management::Manageable* parent; + bool lastNode; //used to set mode on queue declare + Broker* broker; //destroy impl that assumes lock is already held: void destroyLH (const string& name); }; -} -} +}} // namespace qpid::broker #endif diff --git a/cpp/src/qpid/broker/QueuedMessage.h b/cpp/src/qpid/broker/QueuedMessage.h new file mode 100644 index 0000000000..35e48b11f3 --- /dev/null +++ b/cpp/src/qpid/broker/QueuedMessage.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QueuedMessage_ +#define _QueuedMessage_ + +#include "qpid/broker/Message.h" + +namespace qpid { +namespace broker { + +class Queue; + +struct QueuedMessage +{ + boost::intrusive_ptr<Message> payload; + framing::SequenceNumber position; + Queue* queue; + + QueuedMessage() : queue(0) {} + QueuedMessage(Queue* q, boost::intrusive_ptr<Message> msg, framing::SequenceNumber sn) : + payload(msg), position(sn), queue(q) {} + QueuedMessage(Queue* q) : queue(q) {} + +}; + inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) { return a.position < b.position; } + +}} + + +#endif diff --git a/cpp/src/qpid/broker/RateFlowcontrol.h b/cpp/src/qpid/broker/RateFlowcontrol.h new file mode 100644 index 0000000000..99f9d2c0c4 --- /dev/null +++ b/cpp/src/qpid/broker/RateFlowcontrol.h @@ -0,0 +1,105 @@ +#ifndef broker_RateFlowcontrol_h +#define broker_RateFlowcontrol_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/Time.h" +#include "qpid/sys/IntegerTypes.h" + +#include <algorithm> + +namespace qpid { +namespace broker { + +// Class to keep track of issuing flow control to make sure that the peer doesn't exceed +// a given message rate +// +// Create the object with the target rate +// Then call sendCredit() whenever credit is issued to the peer +// Call receivedMessage() whenever a message is received, it returns the credit to issue. +// +// sentCredit() be sensibly called with a 0 parameter to indicate +// that we sent credit but treat it as if the value was 0 (we may do this at the start of the connection +// to allow our peer to send messages) +// +// receivedMessage() can be called with 0 to indicate that we've not received a message, but +// tell me what credit I can send. +class RateFlowcontrol { + uint32_t rate; // messages per second + uint32_t maxCredit; // max credit issued to client (issued at start) + uint32_t requestedCredit; + qpid::sys::AbsTime creditSent; + +public: + RateFlowcontrol(uint32_t r) : + rate(r), + maxCredit(0), + requestedCredit(0), + creditSent(qpid::sys::FAR_FUTURE) + {} + + uint32_t getRate() const { + return rate; + } + void sentCredit(const qpid::sys::AbsTime& t, uint32_t credit); + uint32_t receivedMessage(const qpid::sys::AbsTime& t, uint32_t msgs); + uint32_t availableCredit(const qpid::sys::AbsTime& t); + bool flowStopped() const; +}; + +inline void RateFlowcontrol::sentCredit(const qpid::sys::AbsTime& t, uint32_t credit) { + // If the client isn't currently requesting credit (ie it's not sent us anything yet) then + // this credit goes to the max credit held by the client (it can't go to reduce credit + // less than 0) + int32_t nextRequestedCredit = requestedCredit - credit; + if ( nextRequestedCredit<0 ) { + requestedCredit = 0; + maxCredit -= nextRequestedCredit; + } else { + requestedCredit = nextRequestedCredit; + } + creditSent = t; +} + +inline uint32_t RateFlowcontrol::availableCredit(const qpid::sys::AbsTime& t) { + qpid::sys::Duration d(creditSent, t); + // Could be -ve before first sentCredit + int64_t toSend = std::min(rate * d / qpid::sys::TIME_SEC, static_cast<int64_t>(requestedCredit)); + return toSend > 0 ? toSend : 0; +} + +inline uint32_t RateFlowcontrol::receivedMessage(const qpid::sys::AbsTime& t, uint32_t msgs) { + requestedCredit +=msgs; + // Don't send credit for every message, only send if more than 0.5s since last credit or + // we've got less than .25 of the max left (heuristic) + return requestedCredit*4 >= maxCredit*3 || qpid::sys::Duration(creditSent, t) >= 500*qpid::sys::TIME_MSEC + ? availableCredit(t) + : 0; +} + +inline bool RateFlowcontrol::flowStopped() const { + return requestedCredit >= maxCredit; +} + +}} + +#endif // broker_RateFlowcontrol_h diff --git a/cpp/src/qpid/broker/RateTracker.cpp b/cpp/src/qpid/broker/RateTracker.cpp new file mode 100644 index 0000000000..048349b658 --- /dev/null +++ b/cpp/src/qpid/broker/RateTracker.cpp @@ -0,0 +1,51 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/RateTracker.h" + +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::TIME_SEC; + +namespace qpid { +namespace broker { + +RateTracker::RateTracker() : currentCount(0), lastCount(0), lastTime(AbsTime::now()) {} + +RateTracker& RateTracker::operator++() +{ + ++currentCount; + return *this; +} + +double RateTracker::sampleRatePerSecond() +{ + int32_t increment = currentCount - lastCount; + AbsTime now = AbsTime::now(); + Duration interval(lastTime, now); + lastCount = currentCount; + lastTime = now; + //if sampling at higher frequency than supported, will just return the number of increments + if (interval < TIME_SEC) return increment; + else if (increment == 0) return 0; + else return increment / (interval / TIME_SEC); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/RateTracker.h b/cpp/src/qpid/broker/RateTracker.h new file mode 100644 index 0000000000..0c20b37312 --- /dev/null +++ b/cpp/src/qpid/broker/RateTracker.h @@ -0,0 +1,57 @@ +#ifndef QPID_BROKER_RATETRACKER_H +#define QPID_BROKER_RATETRACKER_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/Time.h" + +namespace qpid { +namespace broker { + +/** + * Simple rate tracker: represents some value that can be incremented, + * then can periodcially sample the rate of increments. + */ +class RateTracker +{ + public: + RateTracker(); + /** + * Increments the count being tracked. Can be called concurrently + * with other calls to this operator as well as with calls to + * sampleRatePerSecond(). + */ + RateTracker& operator++(); + /** + * Returns the rate of increments per second since last + * called. Calls to this method should be serialised, but can be + * called concurrently with the increment operator + */ + double sampleRatePerSecond(); + private: + volatile int32_t currentCount; + int32_t lastCount; + qpid::sys::AbsTime lastTime; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_RATETRACKER_H*/ diff --git a/cpp/src/qpid/broker/RecoverableExchange.h b/cpp/src/qpid/broker/RecoverableExchange.h index 76d0d2ecdf..ca6cc1541e 100644 --- a/cpp/src/qpid/broker/RecoverableExchange.h +++ b/cpp/src/qpid/broker/RecoverableExchange.h @@ -40,7 +40,9 @@ public: /** * Recover binding. Nb: queue must have been recovered earlier. */ - virtual void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args) = 0; + virtual void bind(const std::string& queue, + const std::string& routingKey, + qpid::framing::FieldTable& args) = 0; virtual ~RecoverableExchange() {}; }; diff --git a/cpp/src/qpid/broker/RecoverableMessage.h b/cpp/src/qpid/broker/RecoverableMessage.h index f755fdf727..c98857ceb0 100644 --- a/cpp/src/qpid/broker/RecoverableMessage.h +++ b/cpp/src/qpid/broker/RecoverableMessage.h @@ -37,6 +37,7 @@ class RecoverableMessage public: typedef boost::shared_ptr<RecoverableMessage> shared_ptr; virtual void setPersistenceId(uint64_t id) = 0; + virtual void setRedelivered() = 0; /** * Used by store to determine whether to load content on recovery * or let message load its own content as and when it requires it. diff --git a/cpp/src/qpid/broker/RecoverableQueue.h b/cpp/src/qpid/broker/RecoverableQueue.h index b32bae7f07..49f05f97a1 100644 --- a/cpp/src/qpid/broker/RecoverableQueue.h +++ b/cpp/src/qpid/broker/RecoverableQueue.h @@ -22,7 +22,7 @@ * */ -#include "RecoverableMessage.h" +#include "qpid/broker/RecoverableMessage.h" #include <boost/shared_ptr.hpp> namespace qpid { diff --git a/cpp/src/qpid/broker/RecoverableTransaction.h b/cpp/src/qpid/broker/RecoverableTransaction.h index 7fe34b6756..1b7d94bd1a 100644 --- a/cpp/src/qpid/broker/RecoverableTransaction.h +++ b/cpp/src/qpid/broker/RecoverableTransaction.h @@ -24,8 +24,8 @@ #include <boost/shared_ptr.hpp> -#include "RecoverableMessage.h" -#include "RecoverableQueue.h" +#include "qpid/broker/RecoverableMessage.h" +#include "qpid/broker/RecoverableQueue.h" namespace qpid { namespace broker { diff --git a/cpp/src/qpid/broker/RecoveredDequeue.cpp b/cpp/src/qpid/broker/RecoveredDequeue.cpp index e2d70964fb..658fd5a89e 100644 --- a/cpp/src/qpid/broker/RecoveredDequeue.cpp +++ b/cpp/src/qpid/broker/RecoveredDequeue.cpp @@ -18,22 +18,29 @@ * under the License. * */ -#include "RecoveredDequeue.h" +#include "qpid/broker/RecoveredDequeue.h" using boost::intrusive_ptr; using namespace qpid::broker; -RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) {} +RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) +{ + queue->recoverPrepared(msg); +} -bool RecoveredDequeue::prepare(TransactionContext*) throw(){ +bool RecoveredDequeue::prepare(TransactionContext*) throw() +{ //should never be called; transaction has already prepared if an enqueue is recovered return false; } -void RecoveredDequeue::commit() throw(){ +void RecoveredDequeue::commit() throw() +{ + queue->enqueueAborted(msg); } -void RecoveredDequeue::rollback() throw(){ +void RecoveredDequeue::rollback() throw() +{ msg->enqueueComplete(); queue->process(msg); } diff --git a/cpp/src/qpid/broker/RecoveredDequeue.h b/cpp/src/qpid/broker/RecoveredDequeue.h index 276e1f4c5c..67b37db5f9 100644 --- a/cpp/src/qpid/broker/RecoveredDequeue.h +++ b/cpp/src/qpid/broker/RecoveredDequeue.h @@ -21,11 +21,11 @@ #ifndef _RecoveredDequeue_ #define _RecoveredDequeue_ -#include "Deliverable.h" -#include "Message.h" -#include "MessageStore.h" -#include "Queue.h" -#include "TxOp.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/TxOp.h" #include <boost/intrusive_ptr.hpp> @@ -45,6 +45,10 @@ namespace qpid { virtual void commit() throw(); virtual void rollback() throw(); virtual ~RecoveredDequeue(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + Queue::shared_ptr getQueue() const { return queue; } + boost::intrusive_ptr<Message> getMessage() const { return msg; } }; } } diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/cpp/src/qpid/broker/RecoveredEnqueue.cpp index 1984a5d4a8..48faa0942c 100644 --- a/cpp/src/qpid/broker/RecoveredEnqueue.cpp +++ b/cpp/src/qpid/broker/RecoveredEnqueue.cpp @@ -18,12 +18,15 @@ * under the License. * */ -#include "RecoveredEnqueue.h" +#include "qpid/broker/RecoveredEnqueue.h" using boost::intrusive_ptr; using namespace qpid::broker; -RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) {} +RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) +{ + queue->recoverPrepared(msg); +} bool RecoveredEnqueue::prepare(TransactionContext*) throw(){ //should never be called; transaction has already prepared if an enqueue is recovered @@ -36,5 +39,6 @@ void RecoveredEnqueue::commit() throw(){ } void RecoveredEnqueue::rollback() throw(){ + queue->enqueueAborted(msg); } diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.h b/cpp/src/qpid/broker/RecoveredEnqueue.h index 6525179769..09f928f098 100644 --- a/cpp/src/qpid/broker/RecoveredEnqueue.h +++ b/cpp/src/qpid/broker/RecoveredEnqueue.h @@ -21,11 +21,11 @@ #ifndef _RecoveredEnqueue_ #define _RecoveredEnqueue_ -#include "Deliverable.h" -#include "Message.h" -#include "MessageStore.h" -#include "Queue.h" -#include "TxOp.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/TxOp.h" #include <boost/intrusive_ptr.hpp> @@ -45,6 +45,11 @@ namespace qpid { virtual void commit() throw(); virtual void rollback() throw(); virtual ~RecoveredEnqueue(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + Queue::shared_ptr getQueue() const { return queue; } + boost::intrusive_ptr<Message> getMessage() const { return msg; } + }; } } diff --git a/cpp/src/qpid/broker/RecoveryManager.h b/cpp/src/qpid/broker/RecoveryManager.h index 7dcbe3a2b0..2929e92250 100644 --- a/cpp/src/qpid/broker/RecoveryManager.h +++ b/cpp/src/qpid/broker/RecoveryManager.h @@ -21,12 +21,12 @@ #ifndef _RecoveryManager_ #define _RecoveryManager_ -#include "RecoverableExchange.h" -#include "RecoverableQueue.h" -#include "RecoverableMessage.h" -#include "RecoverableTransaction.h" -#include "RecoverableConfig.h" -#include "TransactionalStore.h" +#include "qpid/broker/RecoverableExchange.h" +#include "qpid/broker/RecoverableQueue.h" +#include "qpid/broker/RecoverableMessage.h" +#include "qpid/broker/RecoverableTransaction.h" +#include "qpid/broker/RecoverableConfig.h" +#include "qpid/broker/TransactionalStore.h" #include "qpid/framing/Buffer.h" namespace qpid { diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index b058978ccf..12ac2d2bfd 100644 --- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -18,21 +18,22 @@ * under the License. * */ -#include "RecoveryManagerImpl.h" - -#include "Message.h" -#include "Queue.h" -#include "Link.h" -#include "Bridge.h" -#include "RecoveredEnqueue.h" -#include "RecoveredDequeue.h" +#include "qpid/broker/RecoveryManagerImpl.h" + +#include "qpid/broker/Message.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/Link.h" +#include "qpid/broker/Bridge.h" +#include "qpid/broker/RecoveredEnqueue.h" +#include "qpid/broker/RecoveredDequeue.h" #include "qpid/framing/reply_exceptions.h" -using namespace qpid; -using namespace qpid::broker; using boost::dynamic_pointer_cast; using boost::intrusive_ptr; +namespace qpid { +namespace broker { + RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, LinkRegistry& _links, DtxManager& _dtxMgr, uint64_t _stagingThreshold) : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr), stagingThreshold(_stagingThreshold) {} @@ -44,10 +45,10 @@ class RecoverableMessageImpl : public RecoverableMessage intrusive_ptr<Message> msg; const uint64_t stagingThreshold; public: - RecoverableMessageImpl(const intrusive_ptr<Message>& _msg, uint64_t _stagingThreshold) - : msg(_msg), stagingThreshold(_stagingThreshold) {} + RecoverableMessageImpl(const intrusive_ptr<Message>& _msg, uint64_t _stagingThreshold); ~RecoverableMessageImpl() {}; void setPersistenceId(uint64_t id); + void setRedelivered(); bool loadContent(uint64_t available); void decodeContent(framing::Buffer& buffer); void recover(Queue::shared_ptr queue); @@ -59,7 +60,7 @@ class RecoverableQueueImpl : public RecoverableQueue { Queue::shared_ptr queue; public: - RecoverableQueueImpl(Queue::shared_ptr& _queue) : queue(_queue) {} + RecoverableQueueImpl(const boost::shared_ptr<Queue>& _queue) : queue(_queue) {} ~RecoverableQueueImpl() {}; void setPersistenceId(uint64_t id); uint64_t getPersistenceId() const; @@ -78,7 +79,7 @@ class RecoverableExchangeImpl : public RecoverableExchange public: RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {} void setPersistenceId(uint64_t id); - void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args); + void bind(const std::string& queue, const std::string& routingKey, qpid::framing::FieldTable& args); }; class RecoverableConfigImpl : public RecoverableConfig @@ -102,18 +103,24 @@ public: RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Buffer& buffer) { - return RecoverableExchange::shared_ptr(new RecoverableExchangeImpl(Exchange::decode(exchanges, buffer), queues)); + Exchange::shared_ptr e = Exchange::decode(exchanges, buffer); + if (e) { + return RecoverableExchange::shared_ptr(new RecoverableExchangeImpl(e, queues)); + } else { + return RecoverableExchange::shared_ptr(); + } } RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& buffer) { - Queue::shared_ptr queue = Queue::decode(queues, buffer); + Queue::shared_ptr queue = Queue::decode(queues, buffer, true); try { Exchange::shared_ptr exchange = exchanges.getDefault(); if (exchange) { exchange->bind(queue, queue->getName(), 0); + queue->bound(exchange->getName(), queue->getName(), framing::FieldTable()); } - } catch (const framing::NotFoundException& e) { + } catch (const framing::NotFoundException& /*e*/) { //assume no default exchange has been declared } return RecoverableQueue::shared_ptr(new RecoverableQueueImpl(queue)); @@ -149,7 +156,16 @@ RecoverableConfig::shared_ptr RecoveryManagerImpl::recoverConfig(framing::Buffer void RecoveryManagerImpl::recoveryComplete() { - //TODO (finalise binding setup etc) + //notify all queues and exchanges + queues.eachQueue(boost::bind(&Queue::recoveryComplete, _1, boost::ref(exchanges))); + exchanges.eachExchange(boost::bind(&Exchange::recoveryComplete, _1, boost::ref(exchanges))); +} + +RecoverableMessageImpl:: RecoverableMessageImpl(const intrusive_ptr<Message>& _msg, uint64_t _stagingThreshold) : msg(_msg), stagingThreshold(_stagingThreshold) +{ + if (!msg->isPersistent()) { + msg->forcePersistent(); // set so that message will get dequeued from store. + } } bool RecoverableMessageImpl::loadContent(uint64_t available) @@ -172,6 +188,11 @@ void RecoverableMessageImpl::setPersistenceId(uint64_t id) msg->setPersistenceId(id); } +void RecoverableMessageImpl::setRedelivered() +{ + msg->redeliver(); +} + void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg) { dynamic_pointer_cast<RecoverableMessageImpl>(msg)->recover(queue); @@ -181,7 +202,7 @@ void RecoverableQueueImpl::setPersistenceId(uint64_t id) { queue->setPersistenceId(id); } - + uint64_t RecoverableQueueImpl::getPersistenceId() const { return queue->getPersistenceId(); @@ -215,10 +236,13 @@ void RecoverableConfigImpl::setPersistenceId(uint64_t id) bridge->setPersistenceId(id); } -void RecoverableExchangeImpl::bind(string& queueName, string& key, framing::FieldTable& args) +void RecoverableExchangeImpl::bind(const string& queueName, + const string& key, + framing::FieldTable& args) { Queue::shared_ptr queue = queues.find(queueName); exchange->bind(queue, key, &args); + queue->bound(exchange->getName(), key, args); } void RecoverableMessageImpl::dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue) @@ -251,3 +275,5 @@ void RecoverableTransactionImpl::enqueue(RecoverableQueue::shared_ptr queue, Rec { dynamic_pointer_cast<RecoverableQueueImpl>(queue)->enqueue(buffer, message); } + +}} diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.h b/cpp/src/qpid/broker/RecoveryManagerImpl.h index cd34d464f5..6fbbfc4a6c 100644 --- a/cpp/src/qpid/broker/RecoveryManagerImpl.h +++ b/cpp/src/qpid/broker/RecoveryManagerImpl.h @@ -22,11 +22,11 @@ #define _RecoveryManagerImpl_ #include <list> -#include "DtxManager.h" -#include "ExchangeRegistry.h" -#include "QueueRegistry.h" -#include "LinkRegistry.h" -#include "RecoveryManager.h" +#include "qpid/broker/DtxManager.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid/broker/LinkRegistry.h" +#include "qpid/broker/RecoveryManager.h" namespace qpid { namespace broker { diff --git a/cpp/src/qpid/broker/RetryList.cpp b/cpp/src/qpid/broker/RetryList.cpp new file mode 100644 index 0000000000..8f600c086d --- /dev/null +++ b/cpp/src/qpid/broker/RetryList.cpp @@ -0,0 +1,60 @@ +/* + * + * 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/RetryList.h" + +namespace qpid { +namespace broker { + +RetryList::RetryList() : urlIndex(0), addressIndex(0) {} + +void RetryList::reset(const std::vector<Url>& u) +{ + urls = u; + urlIndex = addressIndex = 0;//reset indices +} + +bool RetryList::next(TcpAddress& address) +{ + while (urlIndex < urls.size()) { + while (addressIndex < urls[urlIndex].size()) { + const TcpAddress* tcp = urls[urlIndex][addressIndex++].get<TcpAddress>(); + if (tcp) { + address = *tcp; + return true; + } + } + urlIndex++; + addressIndex = 0; + } + + urlIndex = addressIndex = 0;//reset indices + return false; +} + +std::ostream& operator<<(std::ostream& os, const RetryList& l) +{ + for (size_t i = 0; i < l.urls.size(); i++) { + os << l.urls[i] << " "; + } + return os; +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/RetryList.h b/cpp/src/qpid/broker/RetryList.h new file mode 100644 index 0000000000..f87adf2c8d --- /dev/null +++ b/cpp/src/qpid/broker/RetryList.h @@ -0,0 +1,54 @@ +#ifndef QPID_BROKER_RETRYLIST_H +#define QPID_BROKER_RETRYLIST_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/Address.h" +#include "qpid/Url.h" + +namespace qpid { +namespace broker { + +/** + * Simple utility for managing a list of urls to try on reconnecting a + * link. Currently only supports TCP urls. + */ +class RetryList +{ + public: + QPID_BROKER_EXTERN RetryList(); + QPID_BROKER_EXTERN void reset(const std::vector<Url>& urls); + QPID_BROKER_EXTERN bool next(TcpAddress& address); + private: + std::vector<Url> urls; + size_t urlIndex; + size_t addressIndex; + + friend std::ostream& operator<<(std::ostream& os, const RetryList& l); +}; + +std::ostream& operator<<(std::ostream& os, const RetryList& l); + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_RETRYLIST_H*/ diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp index 136cf6f785..0e509c8d93 100644 --- a/cpp/src/qpid/broker/SaslAuthenticator.cpp +++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp @@ -19,17 +19,25 @@ * */ -#include "config.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include "Connection.h" +#include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" +#include <boost/format.hpp> #if HAVE_SASL #include <sasl/sasl.h> +#include "qpid/sys/cyrus/CyrusSecurityLayer.h" +using qpid::sys::cyrus::CyrusSecurityLayer; #endif using namespace qpid::framing; +using qpid::sys::SecurityLayer; +using boost::format; +using boost::str; namespace qpid { namespace broker { @@ -39,12 +47,15 @@ class NullAuthenticator : public SaslAuthenticator { Connection& connection; framing::AMQP_ClientProxy::Connection client; + std::string realm; + const bool encrypt; public: - NullAuthenticator(Connection& connection); + NullAuthenticator(Connection& connection, bool encrypt); ~NullAuthenticator(); void getMechanisms(framing::Array& mechanisms); void start(const std::string& mechanism, const std::string& response); void step(const std::string&) {} + std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize); }; #if HAVE_SASL @@ -54,62 +65,132 @@ class CyrusAuthenticator : public SaslAuthenticator sasl_conn_t *sasl_conn; Connection& connection; framing::AMQP_ClientProxy::Connection client; + const bool encrypt; void processAuthenticationStep(int code, const char *challenge, unsigned int challenge_len); public: - CyrusAuthenticator(Connection& connection); + CyrusAuthenticator(Connection& connection, bool encrypt); ~CyrusAuthenticator(); void init(); void getMechanisms(framing::Array& mechanisms); void start(const std::string& mechanism, const std::string& response); void step(const std::string& response); + void getUid(std::string& uid); + void getError(std::string& error); + std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize); }; +bool SaslAuthenticator::available(void) +{ + return true; +} + +// Initialize the SASL mechanism; throw if it fails. +void SaslAuthenticator::init(const std::string& saslName) +{ + int code = sasl_server_init(NULL, saslName.c_str()); + if (code != SASL_OK) { + // TODO: Figure out who owns the char* returned by + // sasl_errstring, though it probably does not matter much + throw Exception(sasl_errstring(code, NULL, NULL)); + } +} + +void SaslAuthenticator::fini(void) +{ + sasl_done(); +} + #else typedef NullAuthenticator CyrusAuthenticator; +bool SaslAuthenticator::available(void) +{ + return false; +} + +void SaslAuthenticator::init(const std::string& /*saslName*/) +{ + throw Exception("Requested authentication but SASL unavailable"); +} + +void SaslAuthenticator::fini(void) +{ + return; +} + #endif std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c) { + static bool needWarning = true; if (c.getBroker().getOptions().auth) { - return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c)); + return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted)); } else { - return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c)); + QPID_LOG(debug, "SASL: No Authentication Performed"); + needWarning = false; + return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted)); } } -NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()) {} +NullAuthenticator::NullAuthenticator(Connection& c, bool e) : connection(c), client(c.getOutput()), + realm(c.getBroker().getOptions().realm), encrypt(e) {} NullAuthenticator::~NullAuthenticator() {} void NullAuthenticator::getMechanisms(Array& mechanisms) { mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS"))); + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("PLAIN")));//useful for testing } void NullAuthenticator::start(const string& mechanism, const string& response) { - QPID_LOG(warning, "SASL: No Authentication Performed"); + if (encrypt) { + QPID_LOG(error, "Rejected un-encrypted connection."); + throw ConnectionForcedException("Connection must be encrypted."); + } if (mechanism == "PLAIN") { // Old behavior - if (response.size() > 0 && response[0] == (char) 0) { - string temp = response.substr(1); - string::size_type i = temp.find((char)0); - string uid = temp.substr(0, i); - string pwd = temp.substr(i + 1); - connection.setUserId(uid); + if (response.size() > 0) { + string uid; + string::size_type i = response.find((char)0); + if (i == 0 && response.size() > 1) { + //no authorization id; use authentication id + i = response.find((char)0, 1); + if (i != string::npos) uid = response.substr(1, i-1); + } else if (i != string::npos) { + //authorization id is first null delimited field + uid = response.substr(0, i); + }//else not a valid SASL PLAIN response, throw error? + if (!uid.empty()) { + //append realm if it has not already been added + i = uid.find(realm); + if (i == string::npos || realm.size() + i < uid.size()) { + uid = str(format("%1%@%2%") % uid % realm); + } + connection.setUserId(uid); + } } } else { connection.setUserId("anonymous"); } - client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax()); +} + + +std::auto_ptr<SecurityLayer> NullAuthenticator::getSecurityLayer(uint16_t) +{ + std::auto_ptr<SecurityLayer> securityLayer; + return securityLayer; } #if HAVE_SASL -CyrusAuthenticator::CyrusAuthenticator(Connection& c) : sasl_conn(0), connection(c), client(c.getOutput()) + +CyrusAuthenticator::CyrusAuthenticator(Connection& c, bool _encrypt) : + sasl_conn(0), connection(c), client(c.getOutput()), encrypt(_encrypt) { init(); } @@ -145,6 +226,39 @@ void CyrusAuthenticator::init() // server error, when one is available throw ConnectionForcedException("Unable to perform authentication"); } + + sasl_security_properties_t secprops; + + //TODO: should the actual SSF values be configurable here? + secprops.min_ssf = encrypt ? 10: 0; + secprops.max_ssf = 256; + + // If the transport provides encryption, notify the SASL library of + // the key length and set the ssf range to prevent double encryption. + sasl_ssf_t external_ssf = (sasl_ssf_t) connection.getSSF(); + if (external_ssf) { + int result = sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, &external_ssf); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result)); + } + + secprops.max_ssf = secprops.min_ssf = 0; + } + + QPID_LOG(debug, "min_ssf: " << secprops.min_ssf << + ", max_ssf: " << secprops.max_ssf << + ", external_ssf: " << external_ssf ); + + secprops.maxbufsize = 65535; + secprops.property_names = 0; + secprops.property_values = 0; + secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */ + + int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: " << result)); + } + } CyrusAuthenticator::~CyrusAuthenticator() @@ -155,6 +269,23 @@ CyrusAuthenticator::~CyrusAuthenticator() } } +void CyrusAuthenticator::getError(string& error) +{ + error = string(sasl_errdetail(sasl_conn)); +} + +void CyrusAuthenticator::getUid(string& uid) +{ + int code; + const void* ptr; + + code = sasl_getprop(sasl_conn, SASL_USERNAME, &ptr); + if (SASL_OK != code) + return; + + uid = string(const_cast<char*>(static_cast<const char*>(ptr))); +} + void CyrusAuthenticator::getMechanisms(Array& mechanisms) { const char *separator = " "; @@ -239,7 +370,7 @@ void CyrusAuthenticator::processAuthenticationStep(int code, const char *challen connection.setUserId(const_cast<char*>(static_cast<const char*>(uid))); - client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax()); } else if (SASL_CONTINUE == code) { string challenge_str(challenge, challenge_len); @@ -264,6 +395,24 @@ void CyrusAuthenticator::processAuthenticationStep(int code, const char *challen } } } + +std::auto_ptr<SecurityLayer> CyrusAuthenticator::getSecurityLayer(uint16_t maxFrameSize) +{ + + const void* value(0); + int result = sasl_getprop(sasl_conn, SASL_SSF, &value); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(sasl_conn))); + } + uint ssf = *(reinterpret_cast<const unsigned*>(value)); + std::auto_ptr<SecurityLayer> securityLayer; + if (ssf) { + QPID_LOG(info, "Installing security layer, SSF: "<< ssf); + securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize)); + } + return securityLayer; +} + #endif }} diff --git a/cpp/src/qpid/broker/SaslAuthenticator.h b/cpp/src/qpid/broker/SaslAuthenticator.h index c2d4ecf7c0..8ddaeb19a4 100644 --- a/cpp/src/qpid/broker/SaslAuthenticator.h +++ b/cpp/src/qpid/broker/SaslAuthenticator.h @@ -24,6 +24,7 @@ #include "qpid/framing/amqp_types.h" #include "qpid/framing/AMQP_ClientProxy.h" #include "qpid/Exception.h" +#include "qpid/sys/SecurityLayer.h" #include <memory> namespace qpid { @@ -38,6 +39,15 @@ public: virtual void getMechanisms(framing::Array& mechanisms) = 0; virtual void start(const std::string& mechanism, const std::string& response) = 0; virtual void step(const std::string& response) = 0; + virtual void getUid(std::string&) {} + virtual void getError(std::string&) {} + virtual std::auto_ptr<qpid::sys::SecurityLayer> getSecurityLayer(uint16_t maxFrameSize) = 0; + + static bool available(void); + + // Initialize the SASL mechanism; throw if it fails. + static void init(const std::string& saslName); + static void fini(void); static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection); }; diff --git a/cpp/src/qpid/broker/SecureConnection.cpp b/cpp/src/qpid/broker/SecureConnection.cpp new file mode 100644 index 0000000000..74aec239ca --- /dev/null +++ b/cpp/src/qpid/broker/SecureConnection.cpp @@ -0,0 +1,87 @@ +/* + * + * 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/SecureConnection.h" +#include "qpid/sys/SecurityLayer.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace broker { + +using qpid::sys::SecurityLayer; + +SecureConnection::SecureConnection() : secured(false) {} + +size_t SecureConnection::decode(const char* buffer, size_t size) +{ + if (!secured && securityLayer.get()) { + //security layer comes into effect on first read after its + //activated + secured = true; + } + if (secured) { + return securityLayer->decode(buffer, size); + } else { + return codec->decode(buffer, size); + } +} + +size_t SecureConnection::encode(const char* buffer, size_t size) +{ + if (secured) { + return securityLayer->encode(buffer, size); + } else { + return codec->encode(buffer, size); + } +} + +bool SecureConnection::canEncode() +{ + if (secured) return securityLayer->canEncode(); + else return codec->canEncode(); +} + +void SecureConnection::closed() +{ + codec->closed(); +} + +bool SecureConnection::isClosed() const +{ + return codec->isClosed(); +} + +framing::ProtocolVersion SecureConnection::getVersion() const +{ + return codec->getVersion(); +} + +void SecureConnection:: setCodec(std::auto_ptr<ConnectionCodec> c) +{ + codec = c; +} + +void SecureConnection::activateSecurityLayer(std::auto_ptr<SecurityLayer> sl) +{ + securityLayer = sl; + securityLayer->init(codec.get()); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SecureConnection.h b/cpp/src/qpid/broker/SecureConnection.h new file mode 100644 index 0000000000..4a0cc50e34 --- /dev/null +++ b/cpp/src/qpid/broker/SecureConnection.h @@ -0,0 +1,60 @@ +#ifndef QPID_BROKER_SECURECONNECTION_H +#define QPID_BROKER_SECURECONNECTION_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/ConnectionCodec.h" +#include <memory> + +namespace qpid { + +namespace sys { +class SecurityLayer; +} + +namespace broker { + +/** + * A ConnectionCodec 'wrapper' that allows a connection to be + * 'secured' e.g. encrypted based on settings negotiatiated at the + * time of establishment. + */ +class SecureConnection : public qpid::sys::ConnectionCodec +{ + public: + SecureConnection(); + size_t decode(const char* buffer, size_t size); + size_t encode(const char* buffer, size_t size); + bool canEncode(); + void closed(); + bool isClosed() const; + framing::ProtocolVersion getVersion() const; + void setCodec(std::auto_ptr<ConnectionCodec>); + void activateSecurityLayer(std::auto_ptr<qpid::sys::SecurityLayer>); + private: + std::auto_ptr<ConnectionCodec> codec; + std::auto_ptr<qpid::sys::SecurityLayer> securityLayer; + bool secured; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_SECURECONNECTION_H*/ diff --git a/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/cpp/src/qpid/broker/SecureConnectionFactory.cpp new file mode 100644 index 0000000000..5a31dbceeb --- /dev/null +++ b/cpp/src/qpid/broker/SecureConnectionFactory.cpp @@ -0,0 +1,73 @@ +/* + * + * 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/SecureConnectionFactory.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/amqp_0_10/Connection.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/SecureConnection.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { + +using framing::ProtocolVersion; +typedef std::auto_ptr<amqp_0_10::Connection> CodecPtr; +typedef std::auto_ptr<SecureConnection> SecureConnectionPtr; +typedef std::auto_ptr<Connection> ConnectionPtr; +typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr; + +SecureConnectionFactory::SecureConnectionFactory(Broker& b) : broker(b) {} + +sys::ConnectionCodec* +SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, + unsigned int conn_ssf ) { + if (broker.getConnectionCounter().allowConnection()) + { + QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused"); + return 0; + } + if (v == ProtocolVersion(0, 10)) { + SecureConnectionPtr sc(new SecureConnection()); + CodecPtr c(new amqp_0_10::Connection(out, id, false)); + ConnectionPtr i(new broker::Connection(c.get(), broker, id, conn_ssf, false)); + i->setSecureConnection(sc.get()); + c->setInputHandler(InputPtr(i.release())); + sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c)); + return sc.release(); + } + return 0; +} + +sys::ConnectionCodec* +SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id, + unsigned int conn_ssf) { + // used to create connections from one broker to another + SecureConnectionPtr sc(new SecureConnection()); + CodecPtr c(new amqp_0_10::Connection(out, id, true)); + ConnectionPtr i(new broker::Connection(c.get(), broker, id, conn_ssf, true )); + i->setSecureConnection(sc.get()); + c->setInputHandler(InputPtr(i.release())); + sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c)); + return sc.release(); +} + + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/IncomingExecutionContext.h b/cpp/src/qpid/broker/SecureConnectionFactory.h index 7380e9ae64..b1af6d4a0f 100644 --- a/cpp/src/qpid/broker/IncomingExecutionContext.h +++ b/cpp/src/qpid/broker/SecureConnectionFactory.h @@ -18,44 +18,33 @@ * under the License. * */ -#ifndef _IncomingExecutionContext_ -#define _IncomingExecutionContext_ +#ifndef _SecureConnectionFactory_ +#define _SecureConnectionFactory_ -#include "Message.h" - -#include "qpid/framing/AccumulatedAck.h" -#include "qpid/framing/SequenceNumber.h" - -#include <boost/intrusive_ptr.hpp> +#include "qpid/sys/ConnectionCodec.h" namespace qpid { namespace broker { +class Broker; -class IncomingExecutionContext +class SecureConnectionFactory : public sys::ConnectionCodec::Factory { - typedef std::list<boost::intrusive_ptr<Message> > Messages; - framing::Window window; - framing::AccumulatedAck completed; - Messages incomplete; - - bool isComplete(const framing::SequenceNumber& command); - void check(); - void wait(); -public: - void noop(); - void flush(); - void sync(); - void sync(const framing::SequenceNumber& point); - framing::SequenceNumber next(); - void complete(const framing::SequenceNumber& command); - void track(boost::intrusive_ptr<Message>); - - const framing::SequenceNumber& getMark(); - framing::SequenceNumberSet getRange(); + public: + SecureConnectionFactory(Broker& b); -}; + sys::ConnectionCodec* + create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, + unsigned int conn_ssf); + sys::ConnectionCodec* + create(sys::OutputControl&, const std::string& id, + unsigned int conn_ssf); + + private: + Broker& broker; +}; }} + #endif diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp index 4d5c4e7537..e9b6aad967 100644 --- a/cpp/src/qpid/broker/SemanticState.cpp +++ b/cpp/src/qpid/broker/SemanticState.cpp @@ -19,21 +19,23 @@ * */ -#include "SessionState.h" -#include "Connection.h" -#include "DeliverableMessage.h" -#include "DtxAck.h" -#include "DtxTimeout.h" -#include "Message.h" -#include "Queue.h" -#include "SessionContext.h" -#include "TxAccept.h" -#include "TxPublish.h" +#include "qpid/broker/SessionState.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/DtxAck.h" +#include "qpid/broker/DtxTimeout.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/SessionContext.h" +#include "qpid/broker/TxAccept.h" +#include "qpid/broker/TxPublish.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/framing/IsInSequenceSet.h" #include "qpid/log/Statement.h" #include "qpid/ptr_map.h" -#include "AclModule.h" +#include "qpid/broker/AclModule.h" #include <boost/bind.hpp> #include <boost/format.hpp> @@ -49,30 +51,35 @@ namespace qpid { namespace broker { -using std::mem_fun_ref; +using namespace std; using boost::intrusive_ptr; +using boost::bind; using namespace qpid::broker; using namespace qpid::framing; using namespace qpid::sys; using qpid::ptr_map_ptr; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +namespace _qmf = qmf::org::apache::qpid::broker; SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss) : session(ss), deliveryAdapter(da), - prefetchSize(0), - prefetchCount(0), tagGenerator("sgen"), dtxSelected(false), - outputTasks(ss) + authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isFederationLink()), + userID(getSession().getConnection().getUserId()), + defaultRealm(getSession().getBroker().getOptions().realm) { - outstanding.reset(); acl = getSession().getBroker().getAcl(); } SemanticState::~SemanticState() { //cancel all consumers for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { - cancel(*ptr_map_ptr(i)); + cancel(i->second); } if (dtxBuffer.get()) { @@ -85,23 +92,20 @@ bool SemanticState::exists(const string& consumerTag){ return consumers.find(consumerTag) != consumers.end(); } -void SemanticState::consume(DeliveryToken::shared_ptr token, string& tagInOut, - Queue::shared_ptr queue, bool nolocal, bool ackRequired, bool acquire, - bool exclusive, const FieldTable*) +void SemanticState::consume(const string& tag, + Queue::shared_ptr queue, bool ackRequired, bool acquire, + bool exclusive, const string& resumeId, uint64_t resumeTtl, const FieldTable& arguments) { - if(tagInOut.empty()) - tagInOut = tagGenerator.generate(); - std::auto_ptr<ConsumerImpl> c(new ConsumerImpl(this, token, tagInOut, queue, ackRequired, nolocal, acquire)); - queue->consume(*c, exclusive);//may throw exception - outputTasks.addOutputTask(c.get()); - consumers.insert(tagInOut, c.release()); + ConsumerImpl::shared_ptr c(new ConsumerImpl(this, tag, queue, ackRequired, acquire, exclusive, resumeId, resumeTtl, arguments)); + queue->consume(c, exclusive);//may throw exception + consumers[tag] = c; } void SemanticState::cancel(const string& tag){ ConsumerImplMap::iterator i = consumers.find(tag); if (i != consumers.end()) { - cancel(*ptr_map_ptr(i)); - consumers.erase(i); + cancel(i->second); + consumers.erase(i); //should cancel all unacked messages for this consumer so that //they are not redelivered on recovery for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::cancel, _1, tag)); @@ -149,7 +153,7 @@ void SemanticState::startDtx(const std::string& xid, DtxManager& mgr, bool join) throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx")); } dtxBuffer = DtxBuffer::shared_ptr(new DtxBuffer(xid)); - txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer); + txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer); if (join) { mgr.join(xid, dtxBuffer); } else { @@ -217,7 +221,7 @@ void SemanticState::resumeDtx(const std::string& xid) checkDtxTimeout(); dtxBuffer->setSuspended(false); - txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer); + txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer); } void SemanticState::checkDtxTimeout() @@ -231,36 +235,70 @@ void SemanticState::checkDtxTimeout() void SemanticState::record(const DeliveryRecord& delivery) { unacked.push_back(delivery); - delivery.addTo(outstanding); } -bool SemanticState::checkPrefetch(intrusive_ptr<Message>& msg) -{ - bool countOk = !prefetchCount || prefetchCount > unacked.size(); - bool sizeOk = !prefetchSize || prefetchSize > msg->contentSize() + outstanding.size || unacked.empty(); - return countOk && sizeOk; -} +const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency"); SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, - DeliveryToken::shared_ptr _token, - const string& _name, - Queue::shared_ptr _queue, - bool ack, - bool _nolocal, - bool _acquire - ) : + const string& _name, + Queue::shared_ptr _queue, + bool ack, + bool _acquire, + bool _exclusive, + const string& _resumeId, + uint64_t _resumeTtl, + const framing::FieldTable& _arguments + + +) : Consumer(_acquire), parent(_parent), - token(_token), name(_name), queue(_queue), ackExpected(ack), - nolocal(_nolocal), acquire(_acquire), blocked(true), - windowing(true), + windowing(true), + exclusive(_exclusive), + resumeId(_resumeId), + resumeTtl(_resumeTtl), + arguments(_arguments), msgCredit(0), - byteCredit(0){} + byteCredit(0), + notifyEnabled(true), + syncFrequency(_arguments.getAsInt(QPID_SYNC_FREQUENCY)), + deliveryCount(0), + mgmtObject(0) +{ + if (parent != 0 && queue.get() != 0 && queue->GetManagementObject() !=0) + { + ManagementAgent* agent = parent->session.getBroker().getManagementAgent(); + qpid::management::Manageable* ms = dynamic_cast<qpid::management::Manageable*> (&(parent->session)); + + if (agent != 0) + { + mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId() ,name, + !acquire, ackExpected, exclusive ,arguments); + agent->addObject (mgmtObject, agent->allocateId(this)); + mgmtObject->set_creditMode("WINDOW"); + } + } +} + +ManagementObject* SemanticState::ConsumerImpl::GetManagementObject (void) const +{ + return (ManagementObject*) mgmtObject; +} + +Manageable::status_t SemanticState::ConsumerImpl::ManagementMethod (uint32_t methodId, Args&, string&) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]"); + + return status; +} + OwnershipToken* SemanticState::ConsumerImpl::getSession() { @@ -270,29 +308,49 @@ OwnershipToken* SemanticState::ConsumerImpl::getSession() bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg) { allocateCredit(msg.payload); - DeliveryId deliveryTag = - parent->deliveryAdapter.deliver(msg, token); + DeliveryRecord record(msg, queue, name, acquire, !ackExpected, windowing); + bool sync = syncFrequency && ++deliveryCount >= syncFrequency; + if (sync) deliveryCount = 0;//reset + parent->deliver(record, sync); + if (!ackExpected && acquire) record.setEnded();//allows message to be released now its been delivered if (windowing || ackExpected || !acquire) { - parent->record(DeliveryRecord(msg, queue, name, token, deliveryTag, acquire, !ackExpected)); + parent->record(record); } if (acquire && !ackExpected) { - queue->dequeue(0, msg.payload); + queue->dequeue(0, msg); } + if (mgmtObject) { mgmtObject->inc_delivered(); } return true; } -bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message> msg) +bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message>) { - return !(nolocal && - &parent->getSession().getConnection() == msg->getPublisher()); + return true; } bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg) { - blocked = !(filter(msg) && checkCredit(msg) && (!ackExpected || parent->checkPrefetch(msg))); + // FIXME aconway 2009-06-08: if we have byte & message credit but + // checkCredit fails because the message is to big, we should + // remain on queue's listener list for possible smaller messages + // in future. + // + blocked = !(filter(msg) && checkCredit(msg)); return !blocked; } +namespace { +struct ConsumerName { + const SemanticState::ConsumerImpl& consumer; + ConsumerName(const SemanticState::ConsumerImpl& ci) : consumer(ci) {} +}; + +ostream& operator<<(ostream& o, const ConsumerName& pc) { + return o << pc.consumer.getName() << " on " + << pc.consumer.getParent().getSession().getSessionId(); +} +} + void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) { uint32_t originalMsgCredit = msgCredit; @@ -303,7 +361,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) if (byteCredit != 0xFFFFFFFF) { byteCredit -= msg->getRequiredCredit(); } - QPID_LOG(debug, "Credit allocated for '" << name << "' on " << parent + QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this) << ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit << " now bytes: " << byteCredit << " msgs: " << msgCredit); @@ -311,23 +369,27 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg) { - if (msgCredit == 0 || (byteCredit != 0xFFFFFFFF && byteCredit < msg->getRequiredCredit())) { - QPID_LOG(debug, "Not enough credit for '" << name << "' on " << parent - << ", bytes: " << byteCredit << " msgs: " << msgCredit); - return false; - } else { - QPID_LOG(debug, "Credit available for '" << name << "' on " << parent - << " bytes: " << byteCredit << " msgs: " << msgCredit); - return true; - } + bool enoughCredit = msgCredit > 0 && + (byteCredit == 0xFFFFFFFF || byteCredit >= msg->getRequiredCredit()); + QPID_LOG(debug, (enoughCredit ? "Sufficient credit for " : "Insufficient credit for ") + << ConsumerName(*this) + << ", have bytes: " << byteCredit << " msgs: " << msgCredit + << ", need " << msg->getRequiredCredit() << " bytes"); + return enoughCredit; } -SemanticState::ConsumerImpl::~ConsumerImpl() {} +SemanticState::ConsumerImpl::~ConsumerImpl() +{ + if (mgmtObject != 0) + mgmtObject->resourceDestroy (); +} -void SemanticState::cancel(ConsumerImpl& c) +void SemanticState::cancel(ConsumerImpl::shared_ptr c) { - outputTasks.removeOutputTask(&c); - Queue::shared_ptr queue = c.getQueue(); + c->disableNotify(); + if (session.isAttached()) + session.getConnection().outputTasks.removeOutputTask(c.get()); + Queue::shared_ptr queue = c->getQueue(); if(queue) { queue->cancel(c); if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) { @@ -345,30 +407,48 @@ void SemanticState::handle(intrusive_ptr<Message> msg) { } else { DeliverableMessage deliverable(msg); route(msg, deliverable); + if (msg->checkContentReleasable()) { + msg->releaseContent(); + } } } +namespace +{ +const std::string nullstring; +} + void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { + msg->setTimestamp(getSession().getBroker().getExpiryPolicy()); + std::string exchangeName = msg->getExchangeName(); - //TODO: the following should be hidden behind message (using MessageAdapter or similar) - if (msg->isA<MessageTransferBody>()) { - msg->getProperties<DeliveryProperties>()->setExchange(exchangeName); - } - if (!cacheExchange || cacheExchange->getName() != exchangeName){ + if (!cacheExchange || cacheExchange->getName() != exchangeName) cacheExchange = session.getBroker().getExchanges().get(exchangeName); + cacheExchange->setProperties(msg); + + /* verify the userid if specified: */ + std::string id = + msg->hasProperties<MessageProperties>() ? msg->getProperties<MessageProperties>()->getUserId() : nullstring; + + if (authMsg && !id.empty() && id != userID && id.append("@").append(defaultRealm) != userID) + { + QPID_LOG(debug, "authorised user id : " << userID << " but user id in message declared as " << id); + throw UnauthorizedAccessException(QPID_MSG("authorised user id : " << userID << " but user id in message declared as " << id)); } - if (acl && acl->doTransferAcl()) - { - if (!acl->authorise(getSession().getConnection().getUserId(),acl::PUBLISH,acl::EXCHANGE,exchangeName, msg->getRoutingKey() )) - throw NotAllowedException("ACL denied exhange publish request"); + if (acl && acl->doTransferAcl()) + { + if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg->getRoutingKey() )) + throw NotAllowedException(QPID_MSG(userID << " cannot publish to " << + exchangeName << " with routing-key " << msg->getRoutingKey())); } cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); if (!strategy.delivered) { - //TODO:if reject-unroutable, then reject - //else route to alternate exchange + //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()); } @@ -380,30 +460,27 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { } void SemanticState::requestDispatch() -{ - for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { - requestDispatch(*ptr_map_ptr(i)); - } +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) + i->second->requestDispatch(); } -void SemanticState::requestDispatch(ConsumerImpl& c) -{ - if(c.isBlocked()) - outputTasks.activateOutput(); - // TODO aconway 2008-07-16: we could directly call - // c.doOutput(); - // since we are in the connections thread but for consistency - // activateOutput() will set it up to be called in the next write idle. - // Current cluster code depends on this, review cluster code to change. +void SemanticState::ConsumerImpl::requestDispatch() +{ + if (blocked) { + parent->session.getConnection().outputTasks.addOutputTask(this); + parent->session.getConnection().outputTasks.activateOutput(); + blocked = false; + } } -void SemanticState::complete(DeliveryRecord& delivery) +bool SemanticState::complete(DeliveryRecord& delivery) { - delivery.subtractFrom(outstanding); ConsumerImplMap::iterator i = consumers.find(delivery.getTag()); if (i != consumers.end()) { - ptr_map_ptr(i)->complete(delivery); + i->second->complete(delivery); } + return delivery.isRedundant(); } void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery) @@ -420,10 +497,9 @@ void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery) void SemanticState::recover(bool requeue) { if(requeue){ - outstanding.reset(); //take copy and clear unacked as requeue may result in redelivery to this session //which will in turn result in additions to unacked - std::list<DeliveryRecord> copy = unacked; + DeliveryRecords copy = unacked; unacked.clear(); for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue)); }else{ @@ -431,27 +507,13 @@ void SemanticState::recover(bool requeue) //unconfirmed messages re redelivered and therefore have their //id adjusted, confirmed messages are not and so the ordering //w.r.t id is lost - unacked.sort(); - } -} - -bool SemanticState::get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected) -{ - QueuedMessage msg = queue->get(); - if(msg.payload){ - DeliveryId myDeliveryTag = deliveryAdapter.deliver(msg, token); - if(ackExpected){ - unacked.push_back(DeliveryRecord(msg, queue, myDeliveryTag)); - } - return true; - }else{ - return false; + sort(unacked.begin(), unacked.end()); } } -DeliveryId SemanticState::redeliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) +void SemanticState::deliver(DeliveryRecord& msg, bool sync) { - return deliveryAdapter.deliver(msg, token); + return deliveryAdapter.deliver(msg, sync); } SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination) @@ -460,7 +522,7 @@ SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination) if (i == consumers.end()) { throw NotFoundException(QPID_MSG("Unknown destination " << destination)); } else { - return *ptr_map_ptr(i); + return *(i->second); } } @@ -478,7 +540,7 @@ void SemanticState::addByteCredit(const std::string& destination, uint32_t value { ConsumerImpl& c = find(destination); c.addByteCredit(value); - requestDispatch(c); + c.requestDispatch(); } @@ -486,7 +548,7 @@ void SemanticState::addMessageCredit(const std::string& destination, uint32_t va { ConsumerImpl& c = find(destination); c.addMessageCredit(value); - requestDispatch(c); + c.requestDispatch(); } void SemanticState::flush(const std::string& destination) @@ -503,30 +565,48 @@ void SemanticState::stop(const std::string& destination) void SemanticState::ConsumerImpl::setWindowMode() { windowing = true; + if (mgmtObject){ + mgmtObject->set_creditMode("WINDOW"); + } } void SemanticState::ConsumerImpl::setCreditMode() { windowing = false; + if (mgmtObject){ + mgmtObject->set_creditMode("CREDIT"); + } } void SemanticState::ConsumerImpl::addByteCredit(uint32_t value) { if (byteCredit != 0xFFFFFFFF) { - byteCredit += value; + if (value == 0xFFFFFFFF) byteCredit = value; + else byteCredit += value; } } void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value) { if (msgCredit != 0xFFFFFFFF) { - msgCredit += value; + if (value == 0xFFFFFFFF) msgCredit = value; + else msgCredit += value; + } +} + +bool SemanticState::ConsumerImpl::haveCredit() +{ + if (msgCredit && byteCredit) { + return true; + } else { + blocked = true; + return false; } } void SemanticState::ConsumerImpl::flush() { - while(queue->dispatch(*this)) + while(haveCredit() && queue->dispatch(shared_from_this())) ; stop(); } @@ -550,20 +630,8 @@ Queue::shared_ptr SemanticState::getQueue(const string& name) const { } AckRange SemanticState::findRange(DeliveryId first, DeliveryId last) -{ - ack_iterator start = find_if(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::matchOrAfter, _1, first)); - ack_iterator end = start; - - if (start != unacked.end()) { - if (first == last) { - //just acked single element (move end past it) - ++end; - } else { - //need to find end (position it just after the last record in range) - end = find_if(start, unacked.end(), boost::bind(&DeliveryRecord::after, _1, last)); - } - } - return AckRange(start, end); +{ + return DeliveryRecord::findRange(unacked, first, last); } void SemanticState::acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired) @@ -591,29 +659,62 @@ void SemanticState::reject(DeliveryId first, DeliveryId last) } bool SemanticState::ConsumerImpl::hasOutput() { - return queue->checkForMessages(*this); + return queue->checkForMessages(shared_from_this()); } bool SemanticState::ConsumerImpl::doOutput() { - //TODO: think through properly - return queue->dispatch(*this); + return haveCredit() && queue->dispatch(shared_from_this()); } -void SemanticState::ConsumerImpl::notify() +void SemanticState::ConsumerImpl::enableNotify() { - //TODO: think through properly - parent->outputTasks.activateOutput(); + Mutex::ScopedLock l(lock); + notifyEnabled = true; } +void SemanticState::ConsumerImpl::disableNotify() +{ + Mutex::ScopedLock l(lock); + notifyEnabled = false; +} -void SemanticState::accepted(DeliveryId first, DeliveryId last) +bool SemanticState::ConsumerImpl::isNotifyEnabled() const { + Mutex::ScopedLock l(lock); + return notifyEnabled; +} + +void SemanticState::ConsumerImpl::notify() { - AckRange range = findRange(first, last); + Mutex::ScopedLock l(lock); + if (notifyEnabled) { + parent->session.getConnection().outputTasks.addOutputTask(this); + parent->session.getConnection().outputTasks.activateOutput(); + } +} + + +// Test that a DeliveryRecord's ID is in a sequence set and some other +// predicate on DeliveryRecord holds. +template <class Predicate> struct IsInSequenceSetAnd { + IsInSequenceSet isInSet; + Predicate predicate; + IsInSequenceSetAnd(const SequenceSet& s, Predicate p) : isInSet(s), predicate(p) {} + bool operator()(DeliveryRecord& dr) { + return isInSet(dr.getId()) && predicate(dr); + } +}; + +template<class Predicate> IsInSequenceSetAnd<Predicate> +isInSequenceSetAnd(const SequenceSet& s, Predicate p) { + return IsInSequenceSetAnd<Predicate>(s,p); +} + +void SemanticState::accepted(const SequenceSet& commands) { if (txBuffer.get()) { //in transactional mode, don't dequeue or remove, just //maintain set of acknowledged messages: - accumulatedAck.add(first, last); + accumulatedAck.add(commands); if (dtxBuffer.get()) { //if enlisted in a dtx, copy the relevant slice from @@ -623,25 +724,48 @@ void SemanticState::accepted(DeliveryId first, DeliveryId last) dtxBuffer->enlist(txAck); //mark the relevant messages as 'ended' in unacked - for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::setEnded)); - //if the messages are already completed, they can be //removed from the record - unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); - + DeliveryRecords::iterator removed = + remove_if(unacked.begin(), unacked.end(), + isInSequenceSetAnd(commands, + bind(&DeliveryRecord::setEnded, _1))); + unacked.erase(removed, unacked.end()); } } else { - for_each(range.start, range.end, boost::bind(&DeliveryRecord::accept, _1, (TransactionContext*) 0)); - unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + DeliveryRecords::iterator removed = + remove_if(unacked.begin(), unacked.end(), + isInSequenceSetAnd(commands, + bind(&DeliveryRecord::accept, _1, + (TransactionContext*) 0))); + unacked.erase(removed, unacked.end()); } } -void SemanticState::completed(DeliveryId first, DeliveryId last) -{ - AckRange range = findRange(first, last); - for_each(range.start, range.end, boost::bind(&SemanticState::complete, this, _1)); - unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); +void SemanticState::completed(const SequenceSet& commands) { + DeliveryRecords::iterator removed = + remove_if(unacked.begin(), unacked.end(), + isInSequenceSetAnd(commands, + bind(&SemanticState::complete, this, _1))); + unacked.erase(removed, unacked.end()); requestDispatch(); } +void SemanticState::attached() +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + i->second->enableNotify(); + session.getConnection().outputTasks.addOutputTask(i->second.get()); + } + session.getConnection().outputTasks.activateOutput(); +} + +void SemanticState::detached() +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + i->second->disableNotify(); + session.getConnection().outputTasks.removeOutputTask(i->second.get()); + } +} + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h index e03d5ec89b..e5e3f909f1 100644 --- a/cpp/src/qpid/broker/SemanticState.h +++ b/cpp/src/qpid/broker/SemanticState.h @@ -22,29 +22,30 @@ * */ -#include "Consumer.h" -#include "Deliverable.h" -#include "DeliveryAdapter.h" -#include "DeliveryRecord.h" -#include "DeliveryToken.h" -#include "DtxBuffer.h" -#include "DtxManager.h" -#include "NameGenerator.h" -#include "Prefetch.h" -#include "TxBuffer.h" +#include "qpid/broker/Consumer.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/DeliveryAdapter.h" +#include "qpid/broker/DeliveryRecord.h" +#include "qpid/broker/DtxBuffer.h" +#include "qpid/broker/DtxManager.h" +#include "qpid/broker/NameGenerator.h" +#include "qpid/broker/TxBuffer.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/SequenceSet.h" #include "qpid/framing/Uuid.h" #include "qpid/sys/AggregateOutput.h" -#include "qpid/shared_ptr.h" -#include "AclModule.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> #include <map> #include <vector> #include <boost/intrusive_ptr.hpp> +#include <boost/cast.hpp> namespace qpid { namespace broker { @@ -55,36 +56,54 @@ class SessionContext; * SemanticState holds the L3 and L4 state of an open session, whether * attached to a channel or suspended. */ -class SemanticState : public sys::OutputTask, - private boost::noncopyable -{ - class ConsumerImpl : public Consumer, public sys::OutputTask +class SemanticState : private boost::noncopyable { + public: + class ConsumerImpl : public Consumer, public sys::OutputTask, + public boost::enable_shared_from_this<ConsumerImpl>, + public management::Manageable { + mutable qpid::sys::Mutex lock; SemanticState* const parent; - const DeliveryToken::shared_ptr token; const string name; const Queue::shared_ptr queue; const bool ackExpected; - const bool nolocal; const bool acquire; bool blocked; bool windowing; + bool exclusive; + string resumeId; + uint64_t resumeTtl; + framing::FieldTable arguments; uint32_t msgCredit; uint32_t byteCredit; + bool notifyEnabled; + const int syncFrequency; + int deliveryCount; + qmf::org::apache::qpid::broker::Subscription* mgmtObject; bool checkCredit(boost::intrusive_ptr<Message>& msg); void allocateCredit(boost::intrusive_ptr<Message>& msg); + bool haveCredit(); public: - ConsumerImpl(SemanticState* parent, DeliveryToken::shared_ptr token, + typedef boost::shared_ptr<ConsumerImpl> shared_ptr; + + ConsumerImpl(SemanticState* parent, const string& name, Queue::shared_ptr queue, - bool ack, bool nolocal, bool acquire); + bool ack, bool acquire, bool exclusive, + const std::string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments); ~ConsumerImpl(); OwnershipToken* getSession(); bool deliver(QueuedMessage& msg); bool filter(boost::intrusive_ptr<Message> msg); bool accept(boost::intrusive_ptr<Message> msg); + + void disableNotify(); + void enableNotify(); void notify(); + bool isNotifyEnabled() const; + + void requestDispatch(); void setWindowMode(); void setCreditMode(); @@ -93,50 +112,68 @@ class SemanticState : public sys::OutputTask, void flush(); void stop(); void complete(DeliveryRecord&); - Queue::shared_ptr getQueue() { return queue; } - bool isBlocked() const { return blocked; } + Queue::shared_ptr getQueue() const { return queue; } + bool isBlocked() const { return blocked; } + bool setBlocked(bool set) { std::swap(set, blocked); return set; } bool hasOutput(); bool doOutput(); + + std::string getName() const { return name; } + + bool isAckExpected() const { return ackExpected; } + bool isAcquire() const { return acquire; } + bool isWindowing() const { return windowing; } + bool isExclusive() const { return exclusive; } + uint32_t getMsgCredit() const { return msgCredit; } + uint32_t getByteCredit() const { return byteCredit; } + std::string getResumeId() const { return resumeId; }; + uint64_t getResumeTtl() const { return resumeTtl; } + const framing::FieldTable& getArguments() const { return arguments; } + + SemanticState& getParent() { return *parent; } + const SemanticState& getParent() const { return *parent; } + // Manageable entry points + management::ManagementObject* GetManagementObject (void) const; + management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); }; - typedef boost::ptr_map<std::string,ConsumerImpl> ConsumerImplMap; + private: + typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap; typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap; SessionContext& session; DeliveryAdapter& deliveryAdapter; - Queue::shared_ptr defaultQueue; ConsumerImplMap consumers; - uint32_t prefetchSize; - uint16_t prefetchCount; - Prefetch outstanding; NameGenerator tagGenerator; - std::list<DeliveryRecord> unacked; + DeliveryRecords unacked; TxBuffer::shared_ptr txBuffer; DtxBuffer::shared_ptr dtxBuffer; bool dtxSelected; DtxBufferMap suspendedXids; framing::SequenceSet accumulatedAck; boost::shared_ptr<Exchange> cacheExchange; - sys::AggregateOutput outputTasks; AclModule* acl; - + const bool authMsg; + const string userID; + const string defaultRealm; + void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy); - void record(const DeliveryRecord& delivery); - bool checkPrefetch(boost::intrusive_ptr<Message>& msg); void checkDtxTimeout(); - ConsumerImpl& find(const std::string& destination); - void complete(DeliveryRecord&); + + bool complete(DeliveryRecord&); AckRange findRange(DeliveryId first, DeliveryId last); void requestDispatch(); - void requestDispatch(ConsumerImpl&); - void cancel(ConsumerImpl&); + void cancel(ConsumerImpl::shared_ptr); public: SemanticState(DeliveryAdapter&, SessionContext&); ~SemanticState(); SessionContext& getSession() { return session; } + const SessionContext& getSession() const { return session; } + + ConsumerImpl& find(const std::string& destination); /** * Get named queue, never returns 0. @@ -146,16 +183,13 @@ class SemanticState : public sys::OutputTask, */ Queue::shared_ptr getQueue(const std::string& name) const; - uint32_t setPrefetchSize(uint32_t size){ return prefetchSize = size; } - uint16_t setPrefetchCount(uint16_t n){ return prefetchCount = n; } - bool exists(const string& consumerTag); - /** - *@param tagInOut - if empty it is updated with the generated token. - */ - void consume(DeliveryToken::shared_ptr token, string& tagInOut, Queue::shared_ptr queue, - bool nolocal, bool ackRequired, bool acquire, bool exclusive, const framing::FieldTable* = 0); + void consume(const string& destination, + Queue::shared_ptr queue, + bool ackRequired, bool acquire, bool exclusive, + const string& resumeId=string(), uint64_t resumeTtl=0, + const framing::FieldTable& = framing::FieldTable()); void cancel(const string& tag); @@ -166,7 +200,6 @@ class SemanticState : public sys::OutputTask, void flush(const std::string& destination); void stop(const std::string& destination); - bool get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected); void startTx(); void commit(MessageStore* const store); void rollback(); @@ -176,17 +209,29 @@ class SemanticState : public sys::OutputTask, void suspendDtx(const std::string& xid); void resumeDtx(const std::string& xid); void recover(bool requeue); - DeliveryId redeliver(QueuedMessage& msg, DeliveryToken::shared_ptr token); + void deliver(DeliveryRecord& message, bool sync); void acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired); void release(DeliveryId first, DeliveryId last, bool setRedelivered); void reject(DeliveryId first, DeliveryId last); void handle(boost::intrusive_ptr<Message> msg); - bool hasOutput() { return outputTasks.hasOutput(); } - bool doOutput() { return outputTasks.doOutput(); } - //final 0-10 spec (completed and accepted are distinct): - void completed(DeliveryId deliveryTag, DeliveryId endTag); - void accepted(DeliveryId deliveryTag, DeliveryId endTag); + void completed(const framing::SequenceSet& commands); + void accepted(const framing::SequenceSet& commands); + + void attached(); + void detached(); + + // Used by cluster to re-create sessions + template <class F> void eachConsumer(F f) { + for(ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); ++i) + f(i->second); + } + DeliveryRecords& getUnacked() { return unacked; } + framing::SequenceSet getAccumulatedAck() const { return accumulatedAck; } + TxBuffer::shared_ptr getTxBuffer() const { return txBuffer; } + void setTxBuffer(const TxBuffer::shared_ptr& txb) { txBuffer = txb; } + void setAccumulatedAck(const framing::SequenceSet& s) { accumulatedAck = s; } + void record(const DeliveryRecord& delivery); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp index 03022b00bb..a7743d95ab 100644 --- a/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/cpp/src/qpid/broker/SessionAdapter.cpp @@ -15,17 +15,23 @@ * limitations under the License. * */ -#include "SessionAdapter.h" -#include "Connection.h" -#include "DeliveryToken.h" -#include "MessageDelivery.h" -#include "Queue.h" +#include "qpid/broker/SessionAdapter.h" +#include "qpid/broker/Connection.h" +#include "qpid/broker/Queue.h" #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" -#include "qpid/framing/constants.h" +#include "qpid/framing/enum.h" #include "qpid/log/Statement.h" -#include "qpid/amqp_0_10/exceptions.h" #include "qpid/framing/SequenceSet.h" +#include "qpid/management/ManagementAgent.h" +#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h" +#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h" +#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h" +#include "qmf/org/apache/qpid/broker/EventQueueDelete.h" +#include "qmf/org/apache/qpid/broker/EventBind.h" +#include "qmf/org/apache/qpid/broker/EventUnbind.h" +#include "qmf/org/apache/qpid/broker/EventSubscribe.h" +#include "qmf/org/apache/qpid/broker/EventUnsubscribe.h" #include <boost/format.hpp> #include <boost/cast.hpp> #include <boost/bind.hpp> @@ -35,6 +41,9 @@ namespace broker { using namespace qpid; using namespace qpid::framing; +using namespace qpid::framing::dtx; +using namespace qpid::management; +namespace _qmf = qmf::org::apache::qpid::broker; typedef std::vector<Queue::shared_ptr> QueueVector; @@ -48,23 +57,24 @@ SessionAdapter::SessionAdapter(SemanticState& s) : dtxImpl(s) {} +static const std::string _TRUE("true"); +static const std::string _FALSE("false"); void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const string& type, const string& alternateExchange, bool passive, bool durable, bool /*autoDelete*/, const FieldTable& args){ - AclModule* acl = getBroker().getAcl(); - if (acl) - { - std::map<std::string, std::string> params; - params.insert(make_pair("TYPE", type)); - params.insert(make_pair("ALT", alternateExchange)); - params.insert(make_pair("PAS", std::string(passive ? "Y" : "N") )); - params.insert(make_pair("DURA", std::string(durable ? "Y" : "N"))); - if (!acl->authorise(getConnection().getUserId(),acl::CREATE,acl::EXCHANGE,exchange,¶ms) ) - throw NotAllowedException("ACL denied exhange declare request"); - } - + AclModule* acl = getBroker().getAcl(); + if (acl) { + 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, std::string(passive ? _TRUE : _FALSE) )); + params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE))); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,¶ms) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange declare request from " << getConnection().getUserId())); + } + //TODO: implement autoDelete Exchange::shared_ptr alternate; if (!alternateExchange.empty()) { @@ -75,21 +85,31 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const checkType(actual, type); checkAlternate(actual, alternate); }else{ + if(exchange.find("amq.") == 0 || exchange.find("qpid.") == 0) { + throw framing::NotAllowedException(QPID_MSG("Exchange names beginning with \"amq.\" or \"qpid.\" are reserved. (exchange=\"" << exchange << "\")")); + } try{ std::pair<Exchange::shared_ptr, bool> response = getBroker().getExchanges().declare(exchange, type, durable, args); if (response.second) { - if (durable) { - getBroker().getStore().create(*response.first, args); - } if (alternate) { response.first->setAlternate(alternate); alternate->incAlternateUsers(); } + if (durable) { + getBroker().getStore().create(*response.first, args); + } } else { checkType(response.first, type); checkAlternate(response.first, alternate); } - }catch(UnknownExchangeTypeException& e){ + + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(), getConnection().getUserId(), exchange, type, + alternateExchange, durable, false, args, + response.second ? "created" : "existing")); + + }catch(UnknownExchangeTypeException& /*e*/){ throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type)); } } @@ -104,57 +124,62 @@ void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchang void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate) { - if (alternate && alternate != exchange->getAlternate()) - throw NotAllowedException( - QPID_MSG("Exchange declared with alternate-exchange " - << exchange->getAlternate()->getName() << ", requested " - << alternate->getName())); + if (alternate && ((exchange->getAlternate() && alternate != exchange->getAlternate()) + || !exchange->getAlternate())) + throw NotAllowedException(QPID_MSG("Exchange declared with alternate-exchange " + << (exchange->getAlternate() ? exchange->getAlternate()->getName() : "<nonexistent>") + << ", requested " + << alternate->getName())); } -void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/){ - - AclModule* acl = getBroker().getAcl(); - if (acl) - { - if (!acl->authorise(getConnection().getUserId(),acl::DELETE,acl::EXCHANGE,name,NULL) ) - throw NotAllowedException("ACL denied exhange delete request"); +void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange delete request from " << getConnection().getUserId())); } - //TODO: implement unused Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); if (exchange->inUseAsAlternate()) throw NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange.")); if (exchange->isDurable()) getBroker().getStore().destroy(*exchange); if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers(); getBroker().getExchanges().destroy(name); -} + + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventExchangeDelete(getConnection().getUrl(), getConnection().getUserId(), name)); +} ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& name) { - - AclModule* acl = getBroker().getAcl(); - if (acl) - { - if (!acl->authorise(getConnection().getUserId(),acl::ACCESS,acl::EXCHANGE,name,NULL) ) - throw NotAllowedException("ACL denied exhange query request"); + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,name,NULL) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange query request from " << getConnection().getUserId())); } try { Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); return ExchangeQueryResult(exchange->getType(), exchange->isDurable(), false, exchange->getArgs()); - } catch (const NotFoundException& e) { + } catch (const NotFoundException& /*e*/) { return ExchangeQueryResult("", false, true, FieldTable()); } } + void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, const string& exchangeName, const string& routingKey, - const FieldTable& arguments){ + const FieldTable& arguments) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey)); - AclModule* acl = getBroker().getAcl(); - if (acl) - { - if (!acl->authorise(getConnection().getUserId(),acl::BIND,acl::EXCHANGE,exchangeName,routingKey) ) - throw NotAllowedException("ACL denied exhange bind request"); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,¶ms)) + throw NotAllowedException(QPID_MSG("ACL denied exchange bind request from " << getConnection().getUserId())); } Queue::shared_ptr queue = getQueue(queueName); @@ -166,30 +191,29 @@ void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, if (exchange->isDurable() && queue->isDurable()) { getBroker().getStore().bind(*exchange, *queue, routingKey, arguments); } + + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventBind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, exchangeRoutingKey, arguments)); } }else{ - throw NotFoundException( - "Bind failed. No such exchange: " + exchangeName); + throw NotFoundException("Bind failed. No such exchange: " + exchangeName); } } -void -SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName, - const string& exchangeName, - const string& routingKey) +void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName, + const string& exchangeName, + const string& routingKey) { - - AclModule* acl = getBroker().getAcl(); - if (acl) - { - std::map<std::string, std::string> params; - params.insert(make_pair("QN", queueName)); - params.insert(make_pair("RKEY", routingKey)); - if (!acl->authorise(getConnection().getUserId(),acl::UNBIND,acl::EXCHANGE,exchangeName,¶ms) ) - throw NotAllowedException("ACL denied exchange unbind request"); + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey)); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_UNBIND,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange unbind request from " << getConnection().getUserId())); } - Queue::shared_ptr queue = getQueue(queueName); if (!queue.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); @@ -197,10 +221,14 @@ SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName, if (!exchange.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); //TODO: revise unbind to rely solely on binding key (not args) - if (exchange->unbind(queue, routingKey, 0) && exchange->isDurable() && queue->isDurable()) { - getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable()); - } + if (exchange->unbind(queue, routingKey, 0)) { + if (exchange->isDurable() && queue->isDurable()) + getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable()); + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventUnbind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, routingKey)); + } } ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string& exchangeName, @@ -208,16 +236,15 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string const std::string& key, const framing::FieldTable& args) { - AclModule* acl = getBroker().getAcl(); - if (acl) - { - std::map<std::string, std::string> params; - params.insert(make_pair("QUEUE", queueName)); - params.insert(make_pair("RKEY", queueName)); - if (!acl->authorise(getConnection().getUserId(),acl::CREATE,acl::EXCHANGE,exchangeName,¶ms) ) - throw NotAllowedException("ACL denied exhange bound request"); + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_QUEUENAME, queueName)); + params.insert(make_pair(acl::PROP_ROUTINGKEY, key)); + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchangeName,¶ms) ) + throw NotAllowedException(QPID_MSG("ACL denied exchange bound request from " << getConnection().getUserId())); } - + Exchange::shared_ptr exchange; try { exchange = getBroker().getExchanges().get(exchangeName); @@ -229,7 +256,7 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string } if (!exchange) { - return ExchangeBoundResult(true, false, false, false, false); + return ExchangeBoundResult(true, (!queueName.empty() && !queue), false, false, false); } else if (!queueName.empty() && !queue) { return ExchangeBoundResult(false, true, false, false, false); } else if (exchange->isBound(queue, key.empty() ? 0 : &key, args.count() > 0 ? &args : &args)) { @@ -268,7 +295,6 @@ void SessionAdapter::QueueHandlerImpl::destroyExclusiveQueues() exclusiveQueues.erase(exclusiveQueues.begin()); } } - bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const { @@ -278,13 +304,12 @@ bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name) { - AclModule* acl = getBroker().getAcl(); - if (acl) - { - if (!acl->authorise(getConnection().getUserId(),acl::ACCESS,acl::QUEUE,name,NULL) ) - throw NotAllowedException("ACL denied queue query request"); + AclModule* acl = getBroker().getAcl(); + if (acl) { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,NULL) ) + throw NotAllowedException(QPID_MSG("ACL denied queue query request from " << getConnection().getUserId())); } - + Queue::shared_ptr queue = session.getBroker().getQueues().find(name); if (queue) { @@ -304,20 +329,23 @@ QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name) } void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& alternateExchange, - bool passive, bool durable, bool exclusive, - bool autoDelete, const qpid::framing::FieldTable& arguments){ - - AclModule* acl = getBroker().getAcl(); - if (acl) - { - std::map<std::string, std::string> params; - params.insert(make_pair("ALT", alternateExchange)); - params.insert(make_pair("PAS", std::string(passive ? "Y" : "N") )); - params.insert(make_pair("DURA", std::string(durable ? "Y" : "N"))); - params.insert(make_pair("EXCLUS", std::string(exclusive ? "Y" : "N"))); - params.insert(make_pair("AUTOD", std::string(autoDelete ? "Y" : "N"))); - if (!acl->authorise(getConnection().getUserId(),acl::CREATE,acl::QUEUE,name,¶ms) ) - throw NotAllowedException("ACL denied queue create request"); + bool passive, bool durable, bool exclusive, + bool autoDelete, const qpid::framing::FieldTable& arguments) +{ + AclModule* acl = getBroker().getAcl(); + if (acl) { + std::map<acl::Property, std::string> params; + params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange)); + params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? _TRUE : _FALSE) )); + 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 NotAllowedException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId())); } Exchange::shared_ptr alternate; @@ -326,17 +354,16 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& } Queue::shared_ptr queue; if (passive && !name.empty()) { - queue = getQueue(name); + queue = getQueue(name); //TODO: check alternate-exchange is as expected } else { - std::pair<Queue::shared_ptr, bool> queue_created = - getBroker().getQueues().declare( - name, durable, - autoDelete, - exclusive ? this : 0); - queue = queue_created.first; - assert(queue); - if (queue_created.second) { // This is a new queue + std::pair<Queue::shared_ptr, bool> queue_created = + getBroker().getQueues().declare(name, durable, + autoDelete, + exclusive ? &session : 0); + queue = queue_created.first; + assert(queue); + if (queue_created.second) { // This is a new queue if (alternate) { queue->setAlternateExchange(alternate); alternate->incAlternateUsers(); @@ -345,48 +372,56 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& //apply settings & create persistent record if required queue_created.first->create(arguments); - //add default binding: - getBroker().getExchanges().getDefault()->bind(queue, name, 0); + //add default binding: + getBroker().getExchanges().getDefault()->bind(queue, name, 0); queue->bound(getBroker().getExchanges().getDefault()->getName(), name, arguments); //handle automatic cleanup: - if (exclusive) { - exclusiveQueues.push_back(queue); - } - } else { - if (exclusive && queue->setExclusiveOwner(this)) { - exclusiveQueues.push_back(queue); + if (exclusive) { + exclusiveQueues.push_back(queue); + } + } else { + if (exclusive && queue->setExclusiveOwner(&session)) { + exclusiveQueues.push_back(queue); } } + + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventQueueDeclare(getConnection().getUrl(), getConnection().getUserId(), + name, durable, exclusive, autoDelete, arguments, + queue_created.second ? "created" : "existing")); } - if (exclusive && !queue->isExclusiveOwner(this)) - throw ResourceLockedException( - QPID_MSG("Cannot grant exclusive access to queue " - << queue->getName())); + + if (exclusive && !queue->isExclusiveOwner(&session)) + throw ResourceLockedException(QPID_MSG("Cannot grant exclusive access to queue " + << queue->getName())); } void SessionAdapter::QueueHandlerImpl::purge(const string& queue){ - AclModule* acl = getBroker().getAcl(); - if (acl) - { - if (!acl->authorise(getConnection().getUserId(),acl::PURGE,acl::QUEUE,queue,NULL) ) - throw NotAllowedException("ACL denied queue purge request"); + AclModule* acl = getBroker().getAcl(); + if (acl) + { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_PURGE,acl::OBJ_QUEUE,queue,NULL) ) + throw NotAllowedException(QPID_MSG("ACL denied queue purge request from " << getConnection().getUserId())); } getQueue(queue)->purge(); } void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty){ - AclModule* acl = getBroker().getAcl(); - if (acl) - { - if (!acl->authorise(getConnection().getUserId(),acl::DELETE,acl::QUEUE,queue,NULL) ) - throw NotAllowedException("ACL denied queue delete request"); + AclModule* acl = getBroker().getAcl(); + if (acl) + { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_QUEUE,queue,NULL) ) + throw NotAllowedException(QPID_MSG("ACL denied queue delete request from " << getConnection().getUserId())); } - ChannelException error(0, ""); Queue::shared_ptr q = getQueue(queue); + if (q->hasExclusiveOwner() && !q->isExclusiveOwner(&session)) + throw ResourceLockedException(QPID_MSG("Cannot delete queue " + << queue << "; it is exclusive to another session")); if(ifEmpty && q->getMessageCount() > 0){ throw PreconditionFailedException("Queue not empty."); }else if(ifUnused && q->getConsumerCount() > 0){ @@ -400,16 +435,18 @@ void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnuse q->destroy(); getBroker().getQueues().destroy(queue); q->unbind(getBroker().getExchanges(), q); + + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventQueueDelete(getConnection().getUrl(), getConnection().getUserId(), queue)); } } - SessionAdapter::MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) : HandlerHelper(s), releaseRedeliveredOp(boost::bind(&SemanticState::release, &state, _1, _2, true)), releaseOp(boost::bind(&SemanticState::release, &state, _1, _2, false)), - rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2)), - acceptOp(boost::bind(&SemanticState::accepted, &state, _1, _2)) + rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2)) {} // @@ -431,37 +468,47 @@ void SessionAdapter::MessageHandlerImpl::release(const SequenceSet& transfers, b void SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, - const string& destination, - uint8_t acceptMode, - uint8_t acquireMode, - bool exclusive, - const string& /*resumeId*/,//TODO implement resume behaviour - uint64_t /*resumeTtl*/, - const FieldTable& arguments) + const string& destination, + uint8_t acceptMode, + uint8_t acquireMode, + bool exclusive, + const string& resumeId, + uint64_t resumeTtl, + const FieldTable& arguments) { - AclModule* acl = getBroker().getAcl(); - if (acl) - { - // add flags as needed - if (!acl->authorise(getConnection().getUserId(),acl::CONSUME,acl::QUEUE,queueName,NULL) ) - throw NotAllowedException("ACL denied Queue subscribe request"); + AclModule* acl = getBroker().getAcl(); + if (acl) + { + if (!acl->authorise(getConnection().getUserId(),acl::ACT_CONSUME,acl::OBJ_QUEUE,queueName,NULL) ) + throw NotAllowedException(QPID_MSG("ACL denied Queue subscribe request from " << getConnection().getUserId())); } Queue::shared_ptr queue = getQueue(queueName); if(!destination.empty() && state.exists(destination)) throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session)) + throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue " + << queue->getName())); + + state.consume(destination, queue, + acceptMode == 0, acquireMode == 0, exclusive, + resumeId, resumeTtl, arguments); - string tag = destination; - state.consume(MessageDelivery::getMessageDeliveryToken(destination, acceptMode, acquireMode), - tag, queue, false, //TODO get rid of no-local - acceptMode == 0, acquireMode == 0, exclusive, &arguments); + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventSubscribe(getConnection().getUrl(), getConnection().getUserId(), + queueName, destination, exclusive, arguments)); } void SessionAdapter::MessageHandlerImpl::cancel(const string& destination ) { state.cancel(destination); + + ManagementAgent* agent = getBroker().getManagementAgent(); + if (agent) + agent->raiseEvent(_qmf::EventUnsubscribe(getConnection().getUrl(), getConnection().getUserId(), destination)); } void @@ -510,8 +557,7 @@ void SessionAdapter::MessageHandlerImpl::stop(const std::string& destination) void SessionAdapter::MessageHandlerImpl::accept(const framing::SequenceSet& commands) { - - commands.for_each(acceptOp); + state.accepted(commands); } framing::MessageAcquireResult SessionAdapter::MessageHandlerImpl::acquire(const framing::SequenceSet& transfers) @@ -595,7 +641,7 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, if (suspend) { throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set.")); } else { - return XaResult(XA_RBROLLBACK); + return XaResult(XA_STATUS_XA_RBROLLBACK); } } else { if (suspend) { @@ -603,10 +649,10 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid, } else { state.endDtx(convert(xid), false); } - return XaResult(XA_OK); + return XaResult(XA_STATUS_XA_OK); } - } catch (const DtxTimeoutException& e) { - return XaResult(XA_RBTIMEOUT); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); } } @@ -623,9 +669,9 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid, } else { state.startDtx(convert(xid), getBroker().getDtxManager(), join); } - return XaResult(XA_OK); - } catch (const DtxTimeoutException& e) { - return XaResult(XA_RBTIMEOUT); + return XaResult(XA_STATUS_XA_OK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); } } @@ -633,9 +679,9 @@ XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid) { try { bool ok = getBroker().getDtxManager().prepare(convert(xid)); - return XaResult(ok ? XA_OK : XA_RBROLLBACK); - } catch (const DtxTimeoutException& e) { - return XaResult(XA_RBTIMEOUT); + return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); } } @@ -644,9 +690,9 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid, { try { bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase); - return XaResult(ok ? XA_OK : XA_RBROLLBACK); - } catch (const DtxTimeoutException& e) { - return XaResult(XA_RBTIMEOUT); + return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); } } @@ -655,9 +701,9 @@ XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid) { try { getBroker().getDtxManager().rollback(convert(xid)); - return XaResult(XA_OK); - } catch (const DtxTimeoutException& e) { - return XaResult(XA_RBTIMEOUT); + return XaResult(XA_STATUS_XA_OK); + } catch (const DtxTimeoutException& /*e*/) { + return XaResult(XA_STATUS_XA_RBTIMEOUT); } } @@ -699,11 +745,11 @@ void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid, Queue::shared_ptr SessionAdapter::HandlerHelper::getQueue(const string& name) const { Queue::shared_ptr queue; if (name.empty()) { - throw amqp_0_10::IllegalArgumentException(QPID_MSG("No queue name specified.")); + throw framing::IllegalArgumentException(QPID_MSG("No queue name specified.")); } else { queue = session.getBroker().getQueues().find(name); if (!queue) - throw amqp_0_10::NotFoundException(QPID_MSG("Queue not found: "<<name)); + throw framing::NotFoundException(QPID_MSG("Queue not found: "<<name)); } return queue; } diff --git a/cpp/src/qpid/broker/SessionAdapter.h b/cpp/src/qpid/broker/SessionAdapter.h index 4eaaf13f8d..b69f258037 100644 --- a/cpp/src/qpid/broker/SessionAdapter.h +++ b/cpp/src/qpid/broker/SessionAdapter.h @@ -19,15 +19,16 @@ * */ -#include "HandlerImpl.h" +#include "qpid/broker/HandlerImpl.h" -#include "ConnectionToken.h" -#include "OwnershipToken.h" +#include "qpid/broker/ConnectionToken.h" +#include "qpid/broker/OwnershipToken.h" #include "qpid/Exception.h" #include "qpid/framing/AMQP_ServerOperations.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/StructHelper.h" +#include <algorithm> #include <vector> #include <boost/function.hpp> #include <boost/shared_ptr.hpp> @@ -68,6 +69,12 @@ class Queue; FileHandler* getFileHandler() { throw framing::NotImplementedException("Class not implemented"); } StreamHandler* getStreamHandler() { throw framing::NotImplementedException("Class not implemented"); } + template <class F> void eachExclusiveQueue(F f) + { + queueImpl.eachExclusiveQueue(f); + } + + private: //common base for utility methods etc that are specific to this adapter struct HandlerHelper : public HandlerImpl @@ -102,14 +109,14 @@ class Queue; const std::string& routingKey, const framing::FieldTable& arguments); private: - void checkType(shared_ptr<Exchange> exchange, const std::string& type); + void checkType(boost::shared_ptr<Exchange> exchange, const std::string& type); - void checkAlternate(shared_ptr<Exchange> exchange, - shared_ptr<Exchange> alternate); + void checkAlternate(boost::shared_ptr<Exchange> exchange, + boost::shared_ptr<Exchange> alternate); }; class QueueHandlerImpl : public QueueHandler, - public HandlerHelper, public OwnershipToken + public HandlerHelper { Broker& broker; std::vector< boost::shared_ptr<Queue> > exclusiveQueues; @@ -130,6 +137,10 @@ class Queue; bool isLocal(const ConnectionToken* t) const; void destroyExclusiveQueues(); + template <class F> void eachExclusiveQueue(F f) + { + std::for_each(exclusiveQueues.begin(), exclusiveQueues.end(), f); + } }; class MessageHandlerImpl : diff --git a/cpp/src/qpid/broker/SessionContext.h b/cpp/src/qpid/broker/SessionContext.h index 7a277964ab..afbbb2cc22 100644 --- a/cpp/src/qpid/broker/SessionContext.h +++ b/cpp/src/qpid/broker/SessionContext.h @@ -26,9 +26,9 @@ #include "qpid/framing/AMQP_ClientProxy.h" #include "qpid/framing/amqp_types.h" #include "qpid/sys/OutputControl.h" -#include "ConnectionState.h" -#include "OwnershipToken.h" - +#include "qpid/broker/ConnectionState.h" +#include "qpid/broker/OwnershipToken.h" +#include "qpid/SessionId.h" #include <boost/noncopyable.hpp> @@ -40,9 +40,12 @@ class SessionContext : public OwnershipToken, public sys::OutputControl public: virtual ~SessionContext(){} virtual bool isLocal(const ConnectionToken* t) const = 0; + virtual bool isAttached() const = 0; virtual ConnectionState& getConnection() = 0; virtual framing::AMQP_ClientProxy& getProxy() = 0; virtual Broker& getBroker() = 0; + virtual uint16_t getChannel() const = 0; + virtual const SessionId& getSessionId() const = 0; }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp index c752f6315b..7106f85807 100644 --- a/cpp/src/qpid/broker/SessionHandler.cpp +++ b/cpp/src/qpid/broker/SessionHandler.cpp @@ -18,9 +18,9 @@ * */ -#include "SessionHandler.h" -#include "SessionState.h" -#include "Connection.h" +#include "qpid/broker/SessionHandler.h" +#include "qpid/broker/SessionState.h" +#include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include <boost/bind.hpp> @@ -34,7 +34,8 @@ using namespace qpid::sys; SessionHandler::SessionHandler(Connection& c, ChannelId ch) : amqp_0_10::SessionHandler(&c.getOutput(), ch), connection(c), - proxy(out) + proxy(out), + clusterOrderProxy(c.getClusterOrderOutput() ? new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0) {} SessionHandler::~SessionHandler() {} @@ -44,12 +45,18 @@ ClassId classId(AMQMethodBody* m) { return m ? m->amqpMethodId() : 0; } MethodId methodId(AMQMethodBody* m) { return m ? m->amqpClassId() : 0; } } // namespace -void SessionHandler::channelException(uint16_t, const std::string&) { - handleDetach(); +void SessionHandler::connectionException(framing::connection::CloseCode code, const std::string& msg) { + // NOTE: must tell the error listener _before_ calling connection.close() + if (connection.getErrorListener()) connection.getErrorListener()->connectionError(msg); + connection.close(code, msg); } -void SessionHandler::connectionException(uint16_t code, const std::string& msg) { - connection.close(code, msg, 0, 0); +void SessionHandler::channelException(framing::session::DetachCode, const std::string& msg) { + if (connection.getErrorListener()) connection.getErrorListener()->sessionError(getChannel(), msg); +} + +void SessionHandler::executionException(framing::execution::ErrorCode, const std::string& msg) { + if (connection.getErrorListener()) connection.getErrorListener()->sessionError(getChannel(), msg); } ConnectionState& SessionHandler::getConnection() { return connection; } @@ -71,6 +78,12 @@ void SessionHandler::setState(const std::string& name, bool force) { session = connection.broker.getSessionManager().attach(*this, id, force); } +void SessionHandler::detaching() +{ + assert(session.get()); + session->disableOutput(); +} + FrameHandler* SessionHandler::getInHandler() { return session.get() ? &session->in : 0; } qpid::SessionState* SessionHandler::getState() { return session.get(); } @@ -78,18 +91,31 @@ void SessionHandler::readyToSend() { if (session.get()) session->readyToSend(); } -// TODO aconway 2008-05-12: hacky - handle attached for bridge clients. -// We need to integrate the client code so we can run a real client -// in the bridge. -// -void SessionHandler::attached(const std::string& name) { - if (session.get()) - checkName(name); - else { +/** + * Used by inter-broker bridges to set up session id and attach + */ +void SessionHandler::attachAs(const std::string& name) +{ + SessionId id(connection.getUserId(), name); + SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig(); + session.reset(new SessionState(connection.getBroker(), *this, id, config)); + sendAttach(false); +} + +/** + * TODO: this is a little ugly, fix it; its currently still relied on + * for 'push' bridges + */ +void SessionHandler::attached(const std::string& name) +{ + if (session.get()) { + amqp_0_10::SessionHandler::attached(name); + } else { SessionId id(connection.getUserId(), name); SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig(); session.reset(new SessionState(connection.getBroker(), *this, id, config)); -} + markReadyToSend(); + } } }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h index 1aa3137fdf..ca6d6bb193 100644 --- a/cpp/src/qpid/broker/SessionHandler.h +++ b/cpp/src/qpid/broker/SessionHandler.h @@ -54,23 +54,42 @@ class SessionHandler : public amqp_0_10::SessionHandler { framing::AMQP_ClientProxy& getProxy() { return proxy; } const framing::AMQP_ClientProxy& getProxy() const { return proxy; } + /** + * If commands are sent based on the local time (e.g. in timers), they don't have + * a well-defined ordering across cluster nodes. + * 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; + } + virtual void handleDetach(); - - // Overrides - void attached(const std::string& name); + void attached(const std::string& name);//used by 'pushing' inter-broker bridges + void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges protected: virtual void setState(const std::string& sessionName, bool force); virtual qpid::SessionState* getState(); virtual framing::FrameHandler* getInHandler(); - virtual void channelException(uint16_t code, const std::string& msg); - virtual void connectionException(uint16_t code, const std::string& msg); + virtual void connectionException(framing::connection::CloseCode code, const std::string& msg); + virtual void channelException(framing::session::DetachCode, const std::string& msg); + virtual void executionException(framing::execution::ErrorCode, const std::string& msg); + virtual void detaching(); virtual void readyToSend(); private: + struct SetChannelProxy : public framing::AMQP_ClientProxy { // Proxy that sets the channel. + framing::ChannelHandler setChannel; + SetChannelProxy(uint16_t ch, framing::FrameHandler* out) + : framing::AMQP_ClientProxy(setChannel), setChannel(ch, out) {} + }; + Connection& connection; framing::AMQP_ClientProxy proxy; std::auto_ptr<SessionState> session; + std::auto_ptr<SetChannelProxy> clusterOrderProxy; }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionManager.cpp b/cpp/src/qpid/broker/SessionManager.cpp index e7190fdae6..996a02f4c6 100644 --- a/cpp/src/qpid/broker/SessionManager.cpp +++ b/cpp/src/qpid/broker/SessionManager.cpp @@ -19,8 +19,8 @@ * */ -#include "SessionManager.h" -#include "SessionState.h" +#include "qpid/broker/SessionManager.h" +#include "qpid/broker/SessionState.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/log/Statement.h" #include "qpid/log/Helpers.h" @@ -86,9 +86,14 @@ void SessionManager::forget(const SessionId& id) { void SessionManager::eraseExpired() { // Called with lock held. if (!detached.empty()) { - Detached::iterator keep = std::lower_bound( - detached.begin(), detached.end(), now(), - boost::bind(std::less<AbsTime>(), boost::bind(&SessionState::expiry, _1), _2)); + // This used to use a more elegant invocation of std::lower_bound + // but violated the strict weak ordering rule which Visual Studio + // enforced. See QPID-1424 for more info should you be tempted to + // replace the loop with something more elegant. + AbsTime now = AbsTime::now(); + Detached::iterator keep = detached.begin(); + while ((keep != detached.end()) && ((*keep).expiry < now)) + keep++; if (detached.begin() != keep) { QPID_LOG(debug, "Expiring sessions: " << log::formatList(detached.begin(), keep)); detached.erase(detached.begin(), keep); diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp index aa6f6b7520..4c5aaf7fc4 100644 --- a/cpp/src/qpid/broker/SessionState.cpp +++ b/cpp/src/qpid/broker/SessionState.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,18 +18,22 @@ * under the License. * */ -#include "SessionState.h" -#include "Broker.h" -#include "ConnectionState.h" -#include "MessageDelivery.h" -#include "SessionManager.h" -#include "SessionHandler.h" +#include "qpid/broker/SessionState.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/ConnectionState.h" +#include "qpid/broker/DeliveryRecord.h" +#include "qpid/broker/SessionManager.h" +#include "qpid/broker/SessionHandler.h" +#include "qpid/broker/RateFlowcontrol.h" +#include "qpid/sys/Timer.h" #include "qpid/framing/AMQContentBody.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/framing/AMQMethodBody.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/ServerInvoker.h" #include "qpid/log/Statement.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/framing/AMQP_ClientProxy.h" #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> @@ -44,36 +48,51 @@ using qpid::management::ManagementAgent; using qpid::management::ManagementObject; using qpid::management::Manageable; using qpid::management::Args; +using qpid::sys::AbsTime; +//using qpid::sys::Timer; +namespace _qmf = qmf::org::apache::qpid::broker; SessionState::SessionState( - Broker& b, SessionHandler& h, const SessionId& id, const SessionState::Configuration& config) + Broker& b, SessionHandler& h, const SessionId& id, const SessionState::Configuration& config) : qpid::SessionState(id, config), broker(b), handler(&h), - ignoring(false), semanticState(*this, *this), adapter(semanticState), msgBuilder(&broker.getStore(), broker.getStagingThreshold()), enqueuedOp(boost::bind(&SessionState::enqueued, this, _1)), - mgmtObject(0) + mgmtObject(0), + rateFlowcontrol(0) { + uint32_t maxRate = broker.getOptions().maxSessionRate; + if (maxRate) { + if (handler->getConnection().getClientThrottling()) { + rateFlowcontrol.reset(new RateFlowcontrol(maxRate)); + } else { + QPID_LOG(warning, getId() << ": Unable to flow control client - client doesn't support"); + } + } Manageable* parent = broker.GetVhostObject (); if (parent != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = getBroker().getManagementAgent(); if (agent != 0) { - mgmtObject = new management::Session (agent, this, parent, getId().getName()); + mgmtObject = new _qmf::Session + (agent, this, parent, getId().getName()); mgmtObject->set_attached (0); mgmtObject->set_detachedLifespan (0); - agent->addObject (mgmtObject); + mgmtObject->clr_expireTime(); + if (rateFlowcontrol) mgmtObject->set_maxClientRate(maxRate); + agent->addObject (mgmtObject, agent->allocateId(this)); } } attach(h); } SessionState::~SessionState() { - // Remove ID from active session list. - broker.getSessionManager().forget(getId()); if (mgmtObject != 0) mgmtObject->resourceDestroy (); + + if (flowControlTimer) + flowControlTimer->cancel(); } AMQP_ClientProxy& SessionState::getProxy() { @@ -81,6 +100,11 @@ AMQP_ClientProxy& SessionState::getProxy() { return handler->getProxy(); } +uint16_t SessionState::getChannel() const { + assert(isAttached()); + return handler->getChannel(); +} + ConnectionState& SessionState::getConnection() { assert(isAttached()); return handler->getConnection(); @@ -92,18 +116,19 @@ bool SessionState::isLocal(const ConnectionToken* t) const } void SessionState::detach() { - // activateOutput can be called in a different thread, lock to protect attached status - Mutex::ScopedLock l(lock); QPID_LOG(debug, getId() << ": detached on broker."); - getConnection().outputTasks.removeOutputTask(&semanticState); + disableOutput(); handler = 0; if (mgmtObject != 0) mgmtObject->set_attached (0); } +void SessionState::disableOutput() +{ + semanticState.detached(); //prevents further activateOutput calls until reattached +} + void SessionState::attach(SessionHandler& h) { - // activateOutput can be called in a different thread, lock to protect attached status - Mutex::ScopedLock l(lock); QPID_LOG(debug, getId() << ": attached on broker."); handler = &h; if (mgmtObject != 0) @@ -114,34 +139,42 @@ void SessionState::attach(SessionHandler& h) { } } +void SessionState::abort() { + if (isAttached()) + getConnection().outputTasks.abort(); +} + void SessionState::activateOutput() { - // activateOutput can be called in a different thread, lock to protect attached status - Mutex::ScopedLock l(lock); - if (isAttached()) + if (isAttached()) getConnection().outputTasks.activateOutput(); } +void SessionState::giveReadCredit(int32_t credit) { + if (isAttached()) + getConnection().outputTasks.giveReadCredit(credit); +} + ManagementObject* SessionState::GetManagementObject (void) const { return (ManagementObject*) mgmtObject; } Manageable::status_t SessionState::ManagementMethod (uint32_t methodId, - Args& /*args*/) + Args& /*args*/, + string& /*text*/) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; switch (methodId) { - case management::Session::METHOD_DETACH : - if (handler != 0) - { + case _qmf::Session::METHOD_DETACH : + if (handler != 0) { handler->sendDetach(); } status = Manageable::STATUS_OK; break; - case management::Session::METHOD_CLOSE : + case _qmf::Session::METHOD_CLOSE : /* if (handler != 0) { @@ -151,8 +184,8 @@ Manageable::status_t SessionState::ManagementMethod (uint32_t methodId, break; */ - case management::Session::METHOD_SOLICITACK : - case management::Session::METHOD_RESETLIFESPAN : + case _qmf::Session::METHOD_SOLICITACK : + case _qmf::Session::METHOD_RESETLIFESPAN : status = Manageable::STATUS_NOT_IMPLEMENTED; break; } @@ -162,18 +195,42 @@ Manageable::status_t SessionState::ManagementMethod (uint32_t methodId, void SessionState::handleCommand(framing::AMQMethodBody* method, const SequenceNumber& id) { Invoker::Result invocation = invoke(adapter, *method); - receiverCompleted(id); + receiverCompleted(id); if (!invocation.wasHandled()) { throw NotImplementedException(QPID_MSG("Not implemented: " << *method)); } else if (invocation.hasResult()) { getProxy().getExecution().result(id, invocation.getResult()); } - if (method->isSync()) { + if (method->isSync()) { incomplete.process(enqueuedOp, true); - sendCompletion(); + sendAcceptAndCompletion(); } } +struct ScheduledCreditTask : public sys::TimerTask { + sys::Timer& timer; + SessionState& sessionState; + ScheduledCreditTask(const qpid::sys::Duration& d, sys::Timer& t, + SessionState& s) : + TimerTask(d), + timer(t), + sessionState(s) + {} + + void fire() { + // This is the best we can currently do to avoid a destruction/fire race + sessionState.getConnection().requestIOProcessing(boost::bind(&ScheduledCreditTask::sendCredit, this)); + } + + void sendCredit() { + if ( !sessionState.processSendCredit(0) ) { + QPID_LOG(warning, sessionState.getId() << ": Reschedule sending credit"); + setupNextFire(); + timer.add(this); + } + } +}; + void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) { if (frame.getBof() && frame.getBos()) //start of frameset @@ -183,14 +240,13 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) if (frame.getEof() && frame.getEos()) {//end of frameset if (frame.getBof()) { //i.e this is a just a command frame, add a dummy header - AMQFrame header; - header.setBody(AMQHeaderBody()); + AMQFrame header((AMQHeaderBody())); header.setBof(false); header.setEof(false); - msg->getFrames().append(header); + msg->getFrames().append(header); } msg->setPublisher(&getConnection()); - semanticState.handle(msg); + semanticState.handle(msg); msgBuilder.end(); if (msg->isEnqueueComplete()) { @@ -199,51 +255,80 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) incomplete.add(msg); } - //hold up execution until async enqueue is complete - if (msg->getFrames().getMethod()->isSync()) { + //hold up execution until async enqueue is complete + if (msg->getFrames().getMethod()->isSync()) { incomplete.process(enqueuedOp, true); - sendCompletion(); + sendAcceptAndCompletion(); } else { incomplete.process(enqueuedOp, false); } } + + // Handle producer session flow control + if (rateFlowcontrol && frame.getBof() && frame.getBos()) { + if ( !processSendCredit(1) ) { + QPID_LOG(debug, getId() << ": Schedule sending credit"); + sys::Timer& timer = getBroker().getTimer(); + // Use heuristic for scheduled credit of time for 50 messages, but not longer than 500ms + sys::Duration d = std::min(sys::TIME_SEC * 50 / rateFlowcontrol->getRate(), 500 * sys::TIME_MSEC); + flowControlTimer = new ScheduledCreditTask(d, timer, *this); + timer.add(flowControlTimer); + } + } +} + +bool SessionState::processSendCredit(uint32_t msgs) +{ + qpid::sys::ScopedLock<Mutex> l(rateLock); + // Check for violating flow control + if ( msgs > 0 && rateFlowcontrol->flowStopped() ) { + QPID_LOG(warning, getId() << ": producer throttling violation"); + // TODO: Probably do message.stop("") first time then disconnect + // See comment on getClusterOrderProxy() in .h file + getClusterOrderProxy().getMessage().stop(""); + return true; + } + AbsTime now = AbsTime::now(); + uint32_t sendCredit = rateFlowcontrol->receivedMessage(now, msgs); + if (mgmtObject) mgmtObject->dec_clientCredit(msgs); + if ( sendCredit>0 ) { + QPID_LOG(debug, getId() << ": send producer credit " << sendCredit); + getClusterOrderProxy().getMessage().flow("", 0, sendCredit); + rateFlowcontrol->sentCredit(now, sendCredit); + if (mgmtObject) mgmtObject->inc_clientCredit(sendCredit); + return true; + } else { + return !rateFlowcontrol->flowStopped() ; + } +} + +void SessionState::sendAcceptAndCompletion() +{ + if (!accepted.empty()) { + getProxy().getMessage().accept(accepted); + accepted.clear(); + } + sendCompletion(); } void SessionState::enqueued(boost::intrusive_ptr<Message> msg) { receiverCompleted(msg->getCommandId()); - if (msg->requiresAccept()) - getProxy().getMessage().accept(SequenceSet(msg->getCommandId())); + if (msg->requiresAccept()) + accepted.add(msg->getCommandId()); } void SessionState::handleIn(AMQFrame& frame) { SequenceNumber commandId = receiverGetCurrent(); - try { - //TODO: make command handling more uniform, regardless of whether - //commands carry content. - AMQMethodBody* m = frame.getMethod(); - if (m == 0 || m->isContentBearing()) { - handleContent(frame, commandId); - } else if (frame.getBof() && frame.getEof()) { - handleCommand(frame.getMethod(), commandId); - } else { - throw InternalErrorException("Cannot handle multi-frame command segments yet"); - } - } catch(const SessionException& e) { - //TODO: better implementation of new exception handling mechanism - - //0-10 final changes the types of exceptions, 'model layer' - //exceptions will all be session exceptions regardless of - //current channel/connection classification - - AMQMethodBody* m = frame.getMethod(); - if (m) { - getProxy().getExecution().exception(e.code, commandId, m->amqpClassId(), m->amqpMethodId(), 0, e.what(), FieldTable()); - } else { - getProxy().getExecution().exception(e.code, commandId, 0, 0, 0, e.what(), FieldTable()); - } - ignoring = true; - handler->sendDetach(); + //TODO: make command handling more uniform, regardless of whether + //commands carry content. + AMQMethodBody* m = frame.getMethod(); + if (m == 0 || m->isContentBearing()) { + handleContent(frame, commandId); + } else if (frame.getBof() && frame.getEof()) { + handleCommand(frame.getMethod(), commandId); + } else { + throw InternalErrorException("Cannot handle multi-frame command segments yet"); } } @@ -252,32 +337,50 @@ void SessionState::handleOut(AMQFrame& frame) { handler->out(frame); } -DeliveryId SessionState::deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) +void SessionState::deliver(DeliveryRecord& msg, bool sync) { uint32_t maxFrameSize = getConnection().getFrameMax(); assert(senderGetCommandPoint().offset == 0); SequenceNumber commandId = senderGetCommandPoint().command; - MessageDelivery::deliver(msg, getProxy().getHandler(), commandId, token, maxFrameSize); + msg.deliver(getProxy().getHandler(), commandId, maxFrameSize); assert(senderGetCommandPoint() == SessionPoint(commandId+1, 0)); // Delivery has moved sendPoint. - return commandId; + if (sync) { + AMQP_ClientProxy::Execution& p(getProxy().getExecution()); + Proxy::ScopedSync s(p); + p.sync(); + } } -void SessionState::sendCompletion() { handler->sendCompletion(); } +void SessionState::sendCompletion() { + handler->sendCompletion(); +} void SessionState::senderCompleted(const SequenceSet& commands) { qpid::SessionState::senderCompleted(commands); - for (SequenceSet::RangeIterator i = commands.rangesBegin(); i != commands.rangesEnd(); i++) - semanticState.completed(i->first(), i->last()); + semanticState.completed(commands); } void SessionState::readyToSend() { QPID_LOG(debug, getId() << ": ready to send, activating output."); assert(handler); - sys::AggregateOutput& tasks = handler->getConnection().outputTasks; - tasks.addOutputTask(&semanticState); - tasks.activateOutput(); + semanticState.attached(); + if (rateFlowcontrol) { + qpid::sys::ScopedLock<Mutex> l(rateLock); + // Issue initial credit - use a heuristic here issue min of 300 messages or 1 secs worth + uint32_t credit = std::min(rateFlowcontrol->getRate(), 300U); + QPID_LOG(debug, getId() << ": Issuing producer message credit " << credit); + // See comment on getClusterOrderProxy() in .h file + getClusterOrderProxy().getMessage().setFlowMode("", 0); + getClusterOrderProxy().getMessage().flow("", 0, credit); + rateFlowcontrol->sentCredit(AbsTime::now(), credit); + if (mgmtObject) mgmtObject->inc_clientCredit(credit); + } } Broker& SessionState::getBroker() { return broker; } +framing::AMQP_ClientProxy& SessionState::getClusterOrderProxy() { + return handler->getClusterOrderProxy(); +} + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h index 96f2e8f512..eade93ddaa 100644 --- a/cpp/src/qpid/broker/SessionState.h +++ b/cpp/src/qpid/broker/SessionState.h @@ -25,16 +25,15 @@ #include "qpid/SessionState.h" #include "qpid/framing/FrameHandler.h" #include "qpid/framing/SequenceSet.h" -#include "qpid/sys/Mutex.h" #include "qpid/sys/Time.h" #include "qpid/management/Manageable.h" -#include "qpid/management/Session.h" -#include "SessionAdapter.h" -#include "DeliveryAdapter.h" -#include "IncompleteMessageList.h" -#include "MessageBuilder.h" -#include "SessionContext.h" -#include "SemanticState.h" +#include "qmf/org/apache/qpid/broker/Session.h" +#include "qpid/broker/SessionAdapter.h" +#include "qpid/broker/DeliveryAdapter.h" +#include "qpid/broker/IncompleteMessageList.h" +#include "qpid/broker/MessageBuilder.h" +#include "qpid/broker/SessionContext.h" +#include "qpid/broker/SemanticState.h" #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> @@ -49,6 +48,10 @@ namespace framing { class AMQP_ClientProxy; } +namespace sys { +class TimerTask; +} + namespace broker { class Broker; @@ -56,12 +59,13 @@ class ConnectionState; class Message; class SessionHandler; class SessionManager; +class RateFlowcontrol; /** * Broker-side session state includes session's handler chains, which * may themselves have state. */ -class SessionState : public qpid::SessionState, +class SessionState : public qpid::SessionState, public SessionContext, public DeliveryAdapter, public management::Manageable, @@ -74,10 +78,14 @@ class SessionState : public qpid::SessionState, void detach(); void attach(SessionHandler& handler); + void disableOutput(); /** @pre isAttached() */ framing::AMQP_ClientProxy& getProxy(); - + + /** @pre isAttached() */ + uint16_t getChannel() const; + /** @pre isAttached() */ ConnectionState& getConnection(); bool isLocal(const ConnectionToken* t) const; @@ -85,22 +93,33 @@ class SessionState : public qpid::SessionState, Broker& getBroker(); /** OutputControl **/ + void abort(); void activateOutput(); + void giveReadCredit(int32_t); void senderCompleted(const framing::SequenceSet& ranges); - + void sendCompletion(); //delivery adapter methods: - DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token); + void deliver(DeliveryRecord&, bool sync); // Manageable entry points management::ManagementObject* GetManagementObject (void) const; management::Manageable::status_t - ManagementMethod (uint32_t methodId, management::Args& args); + ManagementMethod (uint32_t methodId, management::Args& args, std::string&); void readyToSend(); + // Used by cluster to create replica sessions. + SemanticState& getSemanticState() { return semanticState; } + boost::intrusive_ptr<Message> getMessageInProgress() { return msgBuilder.getMessage(); } + SessionAdapter& getSessionAdapter() { return adapter; } + + bool processSendCredit(uint32_t msgs); + + const SessionId& getSessionId() const { return getId(); } + private: void handleCommand(framing::AMQMethodBody* method, const framing::SequenceNumber& id); @@ -114,20 +133,34 @@ class SessionState : public qpid::SessionState, void handleInLast(framing::AMQFrame& frame); void handleOutLast(framing::AMQFrame& frame); + void sendAcceptAndCompletion(); + + /** + * If commands are sent based on the local time (e.g. in timers), they don't have + * a well-defined ordering across cluster nodes. + * 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(); + Broker& broker; - SessionHandler* handler; + SessionHandler* handler; sys::AbsTime expiry; // Used by SessionManager. - sys::Mutex lock; - bool ignoring; - std::string name; SemanticState semanticState; SessionAdapter adapter; MessageBuilder msgBuilder; IncompleteMessageList incomplete; IncompleteMessageList::CompletionListener enqueuedOp; - management::Session* mgmtObject; + qmf::org::apache::qpid::broker::Session* mgmtObject; + qpid::framing::SequenceSet accepted; + + // State used for producer flow control (rate limited) + qpid::sys::Mutex rateLock; + boost::scoped_ptr<RateFlowcontrol> rateFlowcontrol; + boost::intrusive_ptr<sys::TimerTask> flowControlTimer; - friend class SessionManager; + friend class SessionManager; }; diff --git a/cpp/src/qpid/broker/SignalHandler.cpp b/cpp/src/qpid/broker/SignalHandler.cpp index fee54cfdfc..b565cfd419 100644 --- a/cpp/src/qpid/broker/SignalHandler.cpp +++ b/cpp/src/qpid/broker/SignalHandler.cpp @@ -18,8 +18,8 @@ * under the License. * */ -#include "SignalHandler.h" -#include "Broker.h" +#include "qpid/broker/SignalHandler.h" +#include "qpid/broker/Broker.h" #include <signal.h> namespace qpid { @@ -36,11 +36,10 @@ void SignalHandler::setBroker(const boost::intrusive_ptr<Broker>& b) { signal(SIGHUP,SIG_IGN); // TODO aconway 2007-07-18: reload config. signal(SIGCHLD,SIG_IGN); - signal(SIGTSTP,SIG_IGN); - signal(SIGTTOU,SIG_IGN); - signal(SIGTTIN,SIG_IGN); } +void SignalHandler::shutdown() { shutdownHandler(0); } + void SignalHandler::shutdownHandler(int) { if (broker.get()) { broker->shutdown(); diff --git a/cpp/src/qpid/broker/SignalHandler.h b/cpp/src/qpid/broker/SignalHandler.h index d2cdfae07c..bbe831b61d 100644 --- a/cpp/src/qpid/broker/SignalHandler.h +++ b/cpp/src/qpid/broker/SignalHandler.h @@ -38,6 +38,9 @@ class SignalHandler /** Set the broker to be shutdown on signals */ static void setBroker(const boost::intrusive_ptr<Broker>& broker); + /** Initiate shut-down of broker */ + static void shutdown(); + private: static void shutdownHandler(int); static boost::intrusive_ptr<Broker> broker; diff --git a/cpp/src/qpid/broker/System.cpp b/cpp/src/qpid/broker/System.cpp index 6c58339432..455ad11cf2 100644 --- a/cpp/src/qpid/broker/System.cpp +++ b/cpp/src/qpid/broker/System.cpp @@ -17,20 +17,22 @@ // under the License. // -#include "System.h" -#include "qpid/agent/ManagementAgent.h" +#include "qpid/broker/System.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/framing/Uuid.h" -#include <sys/utsname.h> +#include "qpid/sys/SystemInfo.h" #include <iostream> #include <fstream> using qpid::management::ManagementAgent; using namespace qpid::broker; using namespace std; +namespace _qmf = qmf::org::apache::qpid::broker; -System::System (string _dataDir) : mgmtObject(0) +System::System (string _dataDir, Broker* broker) : mgmtObject(0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = broker ? broker->getManagementAgent() : 0; if (agent != 0) { @@ -62,18 +64,20 @@ System::System (string _dataDir) : mgmtObject(0) } } - mgmtObject = new management::System (agent, this, systemId); - struct utsname _uname; - if (uname (&_uname) == 0) - { - mgmtObject->set_osName (std::string (_uname.sysname)); - mgmtObject->set_nodeName (std::string (_uname.nodename)); - mgmtObject->set_release (std::string (_uname.release)); - mgmtObject->set_version (std::string (_uname.version)); - mgmtObject->set_machine (std::string (_uname.machine)); - } + mgmtObject = new _qmf::System (agent, this, systemId); + std::string sysname, nodename, release, version, machine; + qpid::sys::SystemInfo::getSystemId (sysname, + nodename, + release, + version, + machine); + mgmtObject->set_osName (sysname); + mgmtObject->set_nodeName (nodename); + mgmtObject->set_release (release); + mgmtObject->set_version (version); + mgmtObject->set_machine (machine); - agent->addObject (mgmtObject, 1, 1); + agent->addObject (mgmtObject, 0x1000000000000001LL); } } diff --git a/cpp/src/qpid/broker/System.h b/cpp/src/qpid/broker/System.h index ef7c6ba73b..0fc2c2bd88 100644 --- a/cpp/src/qpid/broker/System.h +++ b/cpp/src/qpid/broker/System.h @@ -21,24 +21,26 @@ // #include "qpid/management/Manageable.h" -#include "qpid/management/System.h" +#include "qmf/org/apache/qpid/broker/System.h" #include <boost/shared_ptr.hpp> #include <string> namespace qpid { namespace broker { +class Broker; + class System : public management::Manageable { private: - management::System* mgmtObject; + qmf::org::apache::qpid::broker::System* mgmtObject; public: typedef boost::shared_ptr<System> shared_ptr; - System (std::string _dataDir); + System (std::string _dataDir, Broker* broker = 0); management::ManagementObject* GetManagementObject (void) const { return mgmtObject; } diff --git a/cpp/src/qpid/broker/Timer.cpp b/cpp/src/qpid/broker/Timer.cpp deleted file mode 100644 index 0b0d3ba63d..0000000000 --- a/cpp/src/qpid/broker/Timer.cpp +++ /dev/null @@ -1,104 +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 "Timer.h" -#include <iostream> - -using boost::intrusive_ptr; -using qpid::sys::AbsTime; -using qpid::sys::Duration; -using qpid::sys::Monitor; -using qpid::sys::Thread; -using namespace qpid::broker; - -TimerTask::TimerTask(Duration timeout) : - duration(timeout), time(AbsTime::now(), timeout), cancelled(false) {} - -TimerTask::TimerTask(AbsTime _time) : - duration(0), time(_time), cancelled(false) {} - -TimerTask::~TimerTask(){} - -void TimerTask::reset() { time = AbsTime(AbsTime::now(), duration); } - -Timer::Timer() : active(false) -{ - start(); -} - -Timer::~Timer() -{ - stop(); -} - -void Timer::run() -{ - Monitor::ScopedLock l(monitor); - while(active){ - if (tasks.empty()) { - monitor.wait(); - } else { - intrusive_ptr<TimerTask> t = tasks.top(); - if (t->cancelled) { - tasks.pop(); - } else if(t->time < AbsTime::now()) { - tasks.pop(); - Monitor::ScopedUnlock u(monitor); - t->fire(); - } else { - monitor.wait(t->time); - } - } - } -} - -void Timer::add(intrusive_ptr<TimerTask> task) -{ - Monitor::ScopedLock l(monitor); - tasks.push(task); - monitor.notify(); -} - -void Timer::start() -{ - Monitor::ScopedLock l(monitor); - if (!active) { - active = true; - runner = Thread(this); - } -} - -void Timer::stop() -{ - { - Monitor::ScopedLock l(monitor); - if (!active) return; - active = false; - monitor.notifyAll(); - } - runner.join(); -} - -bool Later::operator()(const intrusive_ptr<TimerTask>& a, - const intrusive_ptr<TimerTask>& b) const -{ - return a.get() && b.get() && a->time > b->time; -} - diff --git a/cpp/src/qpid/broker/Timer.h b/cpp/src/qpid/broker/Timer.h deleted file mode 100644 index f702f0f32d..0000000000 --- a/cpp/src/qpid/broker/Timer.h +++ /dev/null @@ -1,79 +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 _Timer_ -#define _Timer_ - -#include "qpid/sys/Monitor.h" -#include "qpid/sys/Thread.h" -#include "qpid/sys/Runnable.h" -#include "qpid/RefCounted.h" - -#include <memory> -#include <queue> - -#include <boost/intrusive_ptr.hpp> - -namespace qpid { -namespace broker { - -struct TimerTask : public RefCounted { - const qpid::sys::Duration duration; - qpid::sys::AbsTime time; - volatile bool cancelled; - - TimerTask(qpid::sys::Duration timeout); - TimerTask(qpid::sys::AbsTime time); - virtual ~TimerTask(); - void reset(); - virtual void fire() = 0; -}; - -struct Later { - bool operator()(const boost::intrusive_ptr<TimerTask>& a, - const boost::intrusive_ptr<TimerTask>& b) const; -}; - -class Timer : private qpid::sys::Runnable { - protected: - qpid::sys::Monitor monitor; - std::priority_queue<boost::intrusive_ptr<TimerTask>, - std::vector<boost::intrusive_ptr<TimerTask> >, - Later> tasks; - qpid::sys::Thread runner; - bool active; - - virtual void run(); - - public: - Timer(); - virtual ~Timer(); - - void add(boost::intrusive_ptr<TimerTask> task); - void start(); - void stop(); - -}; - - -}} - - -#endif diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp index 48d6e88503..dd57549b5d 100644 --- a/cpp/src/qpid/broker/TopicExchange.cpp +++ b/cpp/src/qpid/broker/TopicExchange.cpp @@ -18,138 +18,249 @@ * under the License. * */ -#include "TopicExchange.h" +#include "qpid/broker/TopicExchange.h" #include <algorithm> -using namespace qpid::broker; + +namespace qpid { +namespace broker { + using namespace qpid::framing; using namespace qpid::sys; +using namespace std; +namespace _qmf = qmf::org::apache::qpid::broker; + // TODO aconway 2006-09-20: More efficient matching algorithm. // Areas for improvement: // - excessive string copying: should be 0 copy, match from original buffer. // - match/lookup: use descision tree or other more efficient structure. -Tokens& Tokens::operator=(const std::string& s) { - clear(); - if (s.empty()) return *this; - std::string::const_iterator i = s.begin(); - while (true) { - // Invariant: i is at the beginning of the next untokenized word. - std::string::const_iterator j = std::find(i, s.end(), '.'); - push_back(std::string(i, j)); - if (j == s.end()) return *this; - i = j + 1; - } - return *this; -} +namespace +{ +const std::string qpidFedOp("qpid.fed.op"); +const std::string qpidFedTags("qpid.fed.tags"); +const std::string qpidFedOrigin("qpid.fed.origin"); -TopicPattern& TopicPattern::operator=(const Tokens& tokens) { - Tokens::operator=(tokens); - normalize(); - return *this; +const std::string fedOpBind("B"); +const std::string fedOpUnbind("U"); +const std::string fedOpReorigin("R"); +const std::string fedOpHello("H"); } + namespace { -const std::string hashmark("#"); -const std::string star("*"); -} +// Iterate over a string of '.'-separated tokens. +struct TokenIterator { + typedef pair<const char*,const char*> Token; + + TokenIterator(const char* b, const char* e) : token(make_pair(b, find(b,e,'.'))), end(e) {} + + bool finished() const { return !token.first; } + + void next() { + if (token.second == end) + token.first = token.second = 0; + else { + token.first=token.second+1; + token.second=(find(token.first, end, '.')); + } + } + + bool match1(char c) const { + return token.second==token.first+1 && *token.first == c; + } + + bool match(const Token& token2) { + ptrdiff_t l=len(); + return l == token2.second-token2.first && + strncmp(token.first, token2.first, l) == 0; + } + + ptrdiff_t len() const { return token.second - token.first; } -void TopicPattern::normalize() { - std::string word; - Tokens::iterator i = begin(); - while (i != end()) { - if (*i == hashmark) { - ++i; - while (i != end()) { - // Invariant: *(i-1)==#, [begin()..i-1] is normalized. - if (*i == star) { // Move * before #. - std::swap(*i, *(i-1)); - ++i; - } else if (*i == hashmark) { - erase(i); // Remove extra # - } else { - break; + Token token; + const char* end; +}; + +class Normalizer : public TokenIterator { + public: + Normalizer(string& p) + : TokenIterator(&p[0], &p[0]+p.size()), pattern(p) + { normalize(); } + + private: + // Apply 2 transformations: #.* -> *.# and #.# -> # + void normalize() { + while (!finished()) { + if (match1('#')) { + const char* hash1=token.first; + next(); + if (!finished()) { + if (match1('#')) { // Erase #.# -> # + pattern.erase(hash1-pattern.data(), 2); + token.first -= 2; + token.second -= 2; + end -= 2; + } + else if (match1('*')) { // Swap #.* -> *.# + swap(*const_cast<char*>(hash1), + *const_cast<char*>(token.first)); + } } } - } else { - i ++; + else + next(); } } -} + string& pattern; +}; -namespace { -// TODO aconway 2006-09-20: Ineficient to convert every routingKey to a string. -// Need StringRef class that operates on a string in place witout copy. -// Should be applied everywhere strings are extracted from frames. -// -bool do_match(Tokens::const_iterator pattern_begin, Tokens::const_iterator pattern_end, Tokens::const_iterator target_begin, Tokens::const_iterator target_end) -{ - // Invariant: [pattern_begin..p) matches [target_begin..t) - Tokens::const_iterator p = pattern_begin; - Tokens::const_iterator t = target_begin; - while (p != pattern_end && t != target_end) - { - if (*p == star || *p == *t) { - ++p, ++t; - } else if (*p == hashmark) { - ++p; - if (do_match(p, pattern_end, t, target_end)) return true; - while (t != target_end) { - ++t; - if (do_match(p, pattern_end, t, target_end)) return true; +class Matcher { + public: + Matcher(const string& p, const string& k) + : matched(), pattern(&p[0], &p[0]+p.size()), key(&k[0], &k[0]+k.size()) + { matched = match(); } + + operator bool() const { return matched; } + + private: + Matcher(const char* bp, const char* ep, const char* bk, const char* ek) + : matched(), pattern(bp,ep), key(bk,ek) { matched = match(); } + + bool match() { + // Invariant: pattern and key match up to but excluding + // pattern.token and key.token + while (!pattern.finished() && !key.finished()) { + if (pattern.match1('*') && !key.finished()) { + pattern.next(); + key.next(); } - return false; - } else { - return false; + else if (pattern.match1('#')) { + pattern.next(); + if (pattern.finished()) return true; // Trailing # matches anything. + while (!key.finished()) { + if (Matcher(pattern.token.first, pattern.end, + key.token.first, key.end)) + return true; + key.next(); + } + return false; + } + else if (pattern.len() == key.len() && + equal(pattern.token.first,pattern.token.second,key.token.first)) { + pattern.next(); + key.next(); + } + else + return false; } + if (!pattern.finished() && pattern.match1('#')) + pattern.next(); // Trailing # matches empty. + return pattern.finished() && key.finished(); } - while (p != pattern_end && *p == hashmark) ++p; // Ignore trailing # - return t == target_end && p == pattern_end; + + bool matched; + TokenIterator pattern, key; +}; } + +// Convert sequences of * and # to a sequence of * followed by a single # +string TopicExchange::normalize(const string& pattern) { + string normal(pattern); + Normalizer n(normal); + return normal; } -bool TopicPattern::match(const Tokens& target) const +bool TopicExchange::match(const string& pattern, const string& key) { - return do_match(begin(), end(), target.begin(), target.end()); + return Matcher(pattern, key); } -TopicExchange::TopicExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +TopicExchange::TopicExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); } TopicExchange::TopicExchange(const std::string& _name, bool _durable, - const FieldTable& _args, Manageable* _parent) : - Exchange(_name, _durable, _args, _parent) + const FieldTable& _args, Manageable* _parent, Broker* b) : + Exchange(_name, _durable, _args, _parent, b) { if (mgmtExchange != 0) mgmtExchange->set_type (typeName); } -bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ - RWlock::ScopedWlock l(lock); - TopicPattern routingPattern(routingKey); - if (isBound(queue, routingPattern)) { - return false; - } else { - Binding::shared_ptr binding (new Binding (routingKey, queue, this)); - bindings[routingPattern].push_back(binding); - if (mgmtExchange != 0) { - mgmtExchange->inc_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->inc_bindingCount(); +bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args) +{ + string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind); + string fedTags(args ? args->getAsString(qpidFedTags) : ""); + string fedOrigin(args ? args->getAsString(qpidFedOrigin) : ""); + bool propagate = false; + bool reallyUnbind; + string routingPattern = normalize(routingKey); + + if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { + RWlock::ScopedWlock l(lock); + if (isBound(queue, routingPattern)) { + return false; + } else { + Binding::shared_ptr binding (new Binding (routingPattern, queue, this, FieldTable(), fedOrigin)); + BoundKey& bk = bindings[routingPattern]; + bk.bindingVector.push_back(binding); + propagate = bk.fedBinding.addOrigin(fedOrigin); + if (mgmtExchange != 0) { + mgmtExchange->inc_bindingCount(); + } + } + } else if (fedOp == fedOpUnbind) { + { + RWlock::ScopedWlock l(lock); + BoundKey& bk = bindings[routingPattern]; + propagate = bk.fedBinding.delOrigin(fedOrigin); + reallyUnbind = bk.fedBinding.count() == 0; + } + if (reallyUnbind) + unbind(queue, routingPattern, 0); + } else if (fedOp == fedOpReorigin) { + /** gather up all the keys that need rebinding in a local vector + * while holding the lock. Then propagate once the lock is + * released + */ + std::vector<std::string> keys2prop; + { + RWlock::ScopedRlock l(lock); + for (BindingMap::iterator iter = bindings.begin(); + iter != bindings.end(); iter++) { + const BoundKey& bk = iter->second; + + if (bk.fedBinding.hasLocal()) { + keys2prop.push_back(iter->first); + } + } + } /* lock dropped */ + for (std::vector<std::string>::const_iterator key = keys2prop.begin(); + key != keys2prop.end(); key++) { + propagateFedOp( *key, string(), fedOpBind, string()); } - return true; } + + routeIVE(); + if (propagate) + propagateFedOp(routingKey, fedTags, fedOp, fedOrigin); + return true; } -bool TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ +bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKey, const FieldTable* /*args*/){ RWlock::ScopedWlock l(lock); - BindingMap::iterator bi = bindings.find(TopicPattern(routingKey)); - Binding::vector& qv(bi->second); + string routingKey = normalize(constRoutingKey); + + BindingMap::iterator bi = bindings.find(routingKey); if (bi == bindings.end()) return false; + BoundKey& bk = bi->second; + Binding::vector& qv(bk.bindingVector); + bool propagate = false; Binding::vector::iterator q; for (q = qv.begin(); q != qv.end(); q++) @@ -157,19 +268,22 @@ bool TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, co break; if(q == qv.end()) return false; qv.erase(q); + propagate = bk.fedBinding.delOrigin(); if(qv.empty()) bindings.erase(bi); if (mgmtExchange != 0) { mgmtExchange->dec_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->dec_bindingCount(); } + + if (propagate) + propagateFedOp(routingKey, string(), fedOpUnbind, string()); return true; } -bool TopicExchange::isBound(Queue::shared_ptr queue, TopicPattern& pattern) +bool TopicExchange::isBound(Queue::shared_ptr queue, const string& pattern) { BindingMap::iterator bi = bindings.find(pattern); if (bi == bindings.end()) return false; - Binding::vector& qv(bi->second); + Binding::vector& qv(bi->second.bindingVector); Binding::vector::iterator q; for (q = qv.begin(); q != qv.end(); q++) if ((*q)->queue == queue) @@ -177,55 +291,41 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, TopicPattern& pattern) return q != qv.end(); } -void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ - RWlock::ScopedRlock l(lock); - uint32_t count(0); - Tokens tokens(routingKey); - - for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { - if (i->first.match(tokens)) { - Binding::vector& qv(i->second); - for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++, count++){ - msg.deliverTo((*j)->queue); - if ((*j)->mgmtBinding != 0) - (*j)->mgmtBinding->inc_msgMatched (); - } - } - } - - if (mgmtExchange != 0) +void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) +{ + Binding::vector mb; + BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >); + PreRoute pr(msg, this); { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - if (count == 0) - { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - else - { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + RWlock::ScopedRlock l(lock); + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (match(i->first, routingKey)) { + Binding::vector& qv(i->second.bindingVector); + for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++){ + b->push_back(*j); + } + } } } + doRoute(msg, b); } bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) { + RWlock::ScopedRlock l(lock); if (routingKey && queue) { - TopicPattern key(*routingKey); + string key(normalize(*routingKey)); return isBound(queue, key); } else if (!routingKey && !queue) { return bindings.size() > 0; } else if (routingKey) { for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { - if (i->first.match(*routingKey)) { + if (match(i->first, *routingKey)) return true; } - } } else { for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { - Binding::vector& qv(i->second); + Binding::vector& qv(i->second.bindingVector); Binding::vector::iterator q; for (q = qv.begin(); q != qv.end(); q++) if ((*q)->queue == queue) @@ -233,10 +333,11 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routing } } return false; + return queue && routingKey; } TopicExchange::~TopicExchange() {} const std::string TopicExchange::typeName("topic"); - +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/TopicExchange.h b/cpp/src/qpid/broker/TopicExchange.h index 2e107142b7..3bbf143889 100644 --- a/cpp/src/qpid/broker/TopicExchange.h +++ b/cpp/src/qpid/broker/TopicExchange.h @@ -23,77 +23,57 @@ #include <map> #include <vector> -#include "Exchange.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Exchange.h" #include "qpid/framing/FieldTable.h" #include "qpid/sys/Monitor.h" -#include "Queue.h" +#include "qpid/broker/Queue.h" namespace qpid { namespace broker { -/** A vector of string tokens */ -class Tokens : public std::vector<std::string> { - public: - Tokens() {}; - // Default copy, assign, dtor are sufficient. - - /** Tokenize s, provides automatic conversion of string to Tokens */ - Tokens(const std::string& s) { operator=(s); } - /** Tokenizing assignment operator s */ - Tokens & operator=(const std::string& s); - - private: - size_t hash; -}; - - -/** - * Tokens that have been normalized as a pattern and can be matched - * with topic Tokens. Normalized meands all sequences of mixed * and - * # are reduced to a series of * followed by at most one #. - */ -class TopicPattern : public Tokens -{ - public: - TopicPattern() {} - // Default copy, assign, dtor are sufficient. - TopicPattern(const Tokens& tokens) { operator=(tokens); } - TopicPattern(const std::string& str) { operator=(str); } - TopicPattern& operator=(const Tokens&); - TopicPattern& operator=(const std::string& str) { return operator=(Tokens(str)); } - - /** Match a topic */ - bool match(const std::string& topic) { return match(Tokens(topic)); } - bool match(const Tokens& topic) const; - - private: - void normalize(); -}; - -class TopicExchange : public virtual Exchange{ - typedef std::map<TopicPattern, Binding::vector> BindingMap; +class TopicExchange : public virtual Exchange { + struct BoundKey { + Binding::vector bindingVector; + FedBinding fedBinding; + }; + typedef std::map<std::string, BoundKey> BindingMap; BindingMap bindings; qpid::sys::RWlock lock; - bool isBound(Queue::shared_ptr queue, TopicPattern& pattern); + bool isBound(Queue::shared_ptr queue, const string& pattern); + public: static const std::string typeName; - TopicExchange(const string& name, management::Manageable* parent = 0); - TopicExchange(const string& _name, bool _durable, - const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); + static QPID_BROKER_EXTERN bool match(const std::string& pattern, const std::string& topic); + static QPID_BROKER_EXTERN std::string normalize(const std::string& pattern); + + QPID_BROKER_EXTERN TopicExchange(const string& name, + management::Manageable* parent = 0, Broker* broker = 0); + QPID_BROKER_EXTERN TopicExchange(const string& _name, + bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0, Broker* broker = 0); virtual std::string getType() const { return typeName; } - virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual bool bind(Queue::shared_ptr queue, + const string& routingKey, + const qpid::framing::FieldTable* args); virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); - virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + QPID_BROKER_EXTERN virtual void route(Deliverable& msg, + const string& routingKey, + const qpid::framing::FieldTable* args); - virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + QPID_BROKER_EXTERN virtual bool isBound(Queue::shared_ptr queue, + const string* const routingKey, + const qpid::framing::FieldTable* const args); - virtual ~TopicExchange(); + QPID_BROKER_EXTERN virtual ~TopicExchange(); + virtual bool supportsDynamicBinding() { return true; } }; diff --git a/cpp/src/qpid/broker/TxAccept.cpp b/cpp/src/qpid/broker/TxAccept.cpp index 82acf61cd1..928ac12c10 100644 --- a/cpp/src/qpid/broker/TxAccept.cpp +++ b/cpp/src/qpid/broker/TxAccept.cpp @@ -18,7 +18,7 @@ * under the License. * */ -#include "TxAccept.h" +#include "qpid/broker/TxAccept.h" #include "qpid/log/Statement.h" using std::bind1st; @@ -26,19 +26,56 @@ using std::bind2nd; using std::mem_fun_ref; using namespace qpid::broker; using qpid::framing::SequenceSet; +using qpid::framing::SequenceNumber; -TxAccept::TxAccept(SequenceSet& _acked, std::list<DeliveryRecord>& _unacked) : - acked(_acked), unacked(_unacked) {} +TxAccept::RangeOp::RangeOp(const AckRange& r) : range(r) {} + +void TxAccept::RangeOp::prepare(TransactionContext* ctxt) +{ + for_each(range.start, range.end, bind(&DeliveryRecord::dequeue, _1, ctxt)); +} + +void TxAccept::RangeOp::commit() +{ + for_each(range.start, range.end, bind(&DeliveryRecord::committed, _1)); + for_each(range.start, range.end, bind(&DeliveryRecord::setEnded, _1)); +} + +TxAccept::RangeOps::RangeOps(DeliveryRecords& u) : unacked(u) {} + +void TxAccept::RangeOps::operator()(SequenceNumber start, SequenceNumber end) +{ + ranges.push_back(RangeOp(DeliveryRecord::findRange(unacked, start, end))); +} + +void TxAccept::RangeOps::prepare(TransactionContext* ctxt) +{ + std::for_each(ranges.begin(), ranges.end(), bind(&RangeOp::prepare, _1, ctxt)); +} + +void TxAccept::RangeOps::commit() +{ + std::for_each(ranges.begin(), ranges.end(), bind(&RangeOp::commit, _1)); + //now remove if isRedundant(): + if (!ranges.empty()) { + DeliveryRecords::iterator begin = ranges.front().range.start; + DeliveryRecords::iterator end = ranges.back().range.end; + DeliveryRecords::iterator removed = remove_if(begin, end, mem_fun_ref(&DeliveryRecord::isRedundant)); + unacked.erase(removed, end); + } +} + +TxAccept::TxAccept(const SequenceSet& _acked, DeliveryRecords& _unacked) : + acked(_acked), unacked(_unacked), ops(unacked) +{ + //populate the ops + acked.for_each(ops); +} bool TxAccept::prepare(TransactionContext* ctxt) throw() { try{ - //dequeue messages from their respective queues: - for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) { - if (i->coveredBy(&acked)) { - i->dequeue(ctxt); - } - } + ops.prepare(ctxt); return true; }catch(const std::exception& e){ QPID_LOG(error, "Failed to prepare: " << e.what()); @@ -51,11 +88,13 @@ bool TxAccept::prepare(TransactionContext* ctxt) throw() void TxAccept::commit() throw() { - for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) { - if (i->coveredBy(&acked)) i->setEnded(); + try { + ops.commit(); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to commit (unknown error)"); } - - unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); } void TxAccept::rollback() throw() {} diff --git a/cpp/src/qpid/broker/TxAccept.h b/cpp/src/qpid/broker/TxAccept.h index 9548c50c2a..314a150176 100644 --- a/cpp/src/qpid/broker/TxAccept.h +++ b/cpp/src/qpid/broker/TxAccept.h @@ -25,8 +25,8 @@ #include <functional> #include <list> #include "qpid/framing/SequenceSet.h" -#include "DeliveryRecord.h" -#include "TxOp.h" +#include "qpid/broker/DeliveryRecord.h" +#include "qpid/broker/TxOp.h" namespace qpid { namespace broker { @@ -34,9 +34,31 @@ namespace qpid { * Defines the transactional behaviour for accepts received by * a transactional channel. */ - class TxAccept : public TxOp{ - framing::SequenceSet& acked; - std::list<DeliveryRecord>& unacked; + class TxAccept : public TxOp { + struct RangeOp + { + AckRange range; + + RangeOp(const AckRange& r); + void prepare(TransactionContext* ctxt); + void commit(); + }; + + struct RangeOps + { + std::vector<RangeOp> ranges; + DeliveryRecords& unacked; + + RangeOps(DeliveryRecords& u); + + void operator()(framing::SequenceNumber start, framing::SequenceNumber end); + void prepare(TransactionContext* ctxt); + void commit(); + }; + + framing::SequenceSet acked; + DeliveryRecords& unacked; + RangeOps ops; public: /** @@ -44,11 +66,15 @@ namespace qpid { * acks received * @param unacked the record of delivered messages */ - TxAccept(framing::SequenceSet& acked, std::list<DeliveryRecord>& unacked); + TxAccept(const framing::SequenceSet& acked, DeliveryRecords& unacked); virtual bool prepare(TransactionContext* ctxt) throw(); virtual void commit() throw(); virtual void rollback() throw(); virtual ~TxAccept(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + // Used by cluster replication. + const framing::SequenceSet& getAcked() const { return acked; } }; } } diff --git a/cpp/src/qpid/broker/TxBuffer.cpp b/cpp/src/qpid/broker/TxBuffer.cpp index 8fe2c17bf0..b509778e89 100644 --- a/cpp/src/qpid/broker/TxBuffer.cpp +++ b/cpp/src/qpid/broker/TxBuffer.cpp @@ -18,10 +18,11 @@ * under the License. * */ -#include "TxBuffer.h" +#include "qpid/broker/TxBuffer.h" #include "qpid/log/Statement.h" #include <boost/mem_fn.hpp> +#include <boost/bind.hpp> using boost::mem_fn; using namespace qpid::broker; @@ -73,3 +74,7 @@ bool TxBuffer::commitLocal(TransactionalStore* const store) } return false; } + +void TxBuffer::accept(TxOpConstVisitor& v) const { + std::for_each(ops.begin(), ops.end(), boost::bind(&TxOp::accept, _1, boost::ref(v))); +} diff --git a/cpp/src/qpid/broker/TxBuffer.h b/cpp/src/qpid/broker/TxBuffer.h index 361c47e92c..d49c8ba16a 100644 --- a/cpp/src/qpid/broker/TxBuffer.h +++ b/cpp/src/qpid/broker/TxBuffer.h @@ -24,8 +24,9 @@ #include <algorithm> #include <functional> #include <vector> -#include "TransactionalStore.h" -#include "TxOp.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/TransactionalStore.h" +#include "qpid/broker/TxOp.h" /** * Represents a single transaction. As such, an instance of this class @@ -68,7 +69,7 @@ namespace qpid { /** * Adds an operation to the transaction. */ - void enlist(TxOp::shared_ptr op); + QPID_BROKER_EXTERN void enlist(TxOp::shared_ptr op); /** * Requests that all ops are prepared. This should @@ -81,7 +82,7 @@ namespace qpid { * @returns true if all the operations prepared * successfully, false if not. */ - bool prepare(TransactionContext* const ctxt); + QPID_BROKER_EXTERN bool prepare(TransactionContext* const ctxt); /** * Signals that the ops all prepared successfully and can @@ -91,7 +92,7 @@ namespace qpid { * Should only be called after a call to prepare() returns * true. */ - void commit(); + QPID_BROKER_EXTERN void commit(); /** * Signals that all ops can be rolled back. @@ -100,13 +101,16 @@ namespace qpid { * returns true (2pc) or instead of a prepare call * ('server-local') */ - void rollback(); + QPID_BROKER_EXTERN void rollback(); /** * Helper method for managing the process of server local * commit */ - bool commitLocal(TransactionalStore* const store); + QPID_BROKER_EXTERN bool commitLocal(TransactionalStore* const store); + + // Used by cluster to replicate transaction status. + void accept(TxOpConstVisitor& v) const; }; } } diff --git a/cpp/src/qpid/broker/TxOp.h b/cpp/src/qpid/broker/TxOp.h index e687c437cc..a8fa1c2621 100644 --- a/cpp/src/qpid/broker/TxOp.h +++ b/cpp/src/qpid/broker/TxOp.h @@ -21,11 +21,13 @@ #ifndef _TxOp_ #define _TxOp_ -#include "TransactionalStore.h" +#include "qpid/broker/TxOpVisitor.h" +#include "qpid/broker/TransactionalStore.h" #include <boost/shared_ptr.hpp> namespace qpid { namespace broker { + class TxOp{ public: typedef boost::shared_ptr<TxOp> shared_ptr; @@ -34,9 +36,11 @@ namespace qpid { virtual void commit() throw() = 0; virtual void rollback() throw() = 0; virtual ~TxOp(){} + + virtual void accept(TxOpConstVisitor&) const = 0; }; - } -} + +}} // namespace qpid::broker #endif diff --git a/cpp/src/qpid/broker/TxOpVisitor.h b/cpp/src/qpid/broker/TxOpVisitor.h new file mode 100644 index 0000000000..ceb894896e --- /dev/null +++ b/cpp/src/qpid/broker/TxOpVisitor.h @@ -0,0 +1,97 @@ +#ifndef QPID_BROKER_TXOPVISITOR_H +#define QPID_BROKER_TXOPVISITOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +namespace qpid { +namespace broker { + +class DtxAck; +class RecoveredDequeue; +class RecoveredEnqueue; +class TxAccept; +class TxPublish; + +/** + * Visitor for TxOp familly of classes. + */ +struct TxOpConstVisitor +{ + virtual ~TxOpConstVisitor() {} + virtual void operator()(const DtxAck&) = 0; + virtual void operator()(const RecoveredDequeue&) = 0; + virtual void operator()(const RecoveredEnqueue&) = 0; + virtual void operator()(const TxAccept&) = 0; + virtual void operator()(const TxPublish&) = 0; +}; + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_TXOPVISITOR_H*/ +#ifndef QPID_BROKER_TXOPVISITOR_H +#define QPID_BROKER_TXOPVISITOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace broker { + +class DtxAck; +class RecoveredDequeue; +class RecoveredEnqueue; +class TxAccept; +class TxPublish; + +/** + * Visitor for TxOp familly of classes. + */ +struct TxOpConstVisitor +{ + virtual ~TxOpConstVisitor() {} + virtual void operator()(const DtxAck&) = 0; + virtual void operator()(const RecoveredDequeue&) = 0; + virtual void operator()(const RecoveredEnqueue&) = 0; + virtual void operator()(const TxAccept&) = 0; + virtual void operator()(const TxPublish&) = 0; +}; + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_TXOPVISITOR_H*/ diff --git a/cpp/src/qpid/broker/TxPublish.cpp b/cpp/src/qpid/broker/TxPublish.cpp index dcee00e803..4b083033ea 100644 --- a/cpp/src/qpid/broker/TxPublish.cpp +++ b/cpp/src/qpid/broker/TxPublish.cpp @@ -19,16 +19,21 @@ * */ #include "qpid/log/Statement.h" -#include "TxPublish.h" +#include "qpid/broker/TxPublish.h" using boost::intrusive_ptr; using namespace qpid::broker; TxPublish::TxPublish(intrusive_ptr<Message> _msg) : msg(_msg) {} -bool TxPublish::prepare(TransactionContext* ctxt) throw(){ +bool TxPublish::prepare(TransactionContext* ctxt) throw() +{ try{ - for_each(queues.begin(), queues.end(), Prepare(ctxt, msg)); + while (!queues.empty()) { + prepare(ctxt, queues.front()); + prepared.push_back(queues.front()); + queues.pop_front(); + } return true; }catch(const std::exception& e){ QPID_LOG(error, "Failed to prepare: " << e.what()); @@ -38,14 +43,33 @@ bool TxPublish::prepare(TransactionContext* ctxt) throw(){ return false; } -void TxPublish::commit() throw(){ - for_each(queues.begin(), queues.end(), Commit(msg)); +void TxPublish::commit() throw() +{ + try { + for_each(prepared.begin(), prepared.end(), Commit(msg)); + if (msg->checkContentReleasable()) { + msg->releaseContent(); + } + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to commit: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to commit (unknown error)"); + } } -void TxPublish::rollback() throw(){ +void TxPublish::rollback() throw() +{ + try { + for_each(prepared.begin(), prepared.end(), Rollback(msg)); + } catch (const std::exception& e) { + QPID_LOG(error, "Failed to complete rollback: " << e.what()); + } catch(...) { + QPID_LOG(error, "Failed to complete rollback (unknown error)"); + } + } -void TxPublish::deliverTo(Queue::shared_ptr& queue){ +void TxPublish::deliverTo(const boost::shared_ptr<Queue>& queue){ if (!queue->isLocal(msg)) { queues.push_back(queue); delivered = true; @@ -54,26 +78,30 @@ void TxPublish::deliverTo(Queue::shared_ptr& queue){ } } -TxPublish::Prepare::Prepare(TransactionContext* _ctxt, intrusive_ptr<Message>& _msg) - : ctxt(_ctxt), msg(_msg){} - -void TxPublish::Prepare::operator()(Queue::shared_ptr& queue){ +void TxPublish::prepare(TransactionContext* ctxt, const boost::shared_ptr<Queue> queue) +{ if (!queue->enqueue(ctxt, msg)){ /** - * if not store then mark message for ack and deleivery once - * commit happens, as async IO will never set it when no store - * exists - */ + * if not store then mark message for ack and deleivery once + * commit happens, as async IO will never set it when no store + * exists + */ msg->enqueueComplete(); } } TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){} -void TxPublish::Commit::operator()(Queue::shared_ptr& queue){ +void TxPublish::Commit::operator()(const boost::shared_ptr<Queue>& queue){ queue->process(msg); } +TxPublish::Rollback::Rollback(intrusive_ptr<Message>& _msg) : msg(_msg){} + +void TxPublish::Rollback::operator()(const boost::shared_ptr<Queue>& queue){ + queue->enqueueAborted(msg); +} + uint64_t TxPublish::contentSize () { return msg->contentSize (); diff --git a/cpp/src/qpid/broker/TxPublish.h b/cpp/src/qpid/broker/TxPublish.h index d2590debfb..b6ab9767ab 100644 --- a/cpp/src/qpid/broker/TxPublish.h +++ b/cpp/src/qpid/broker/TxPublish.h @@ -21,11 +21,12 @@ #ifndef _TxPublish_ #define _TxPublish_ -#include "Queue.h" -#include "Deliverable.h" -#include "Message.h" -#include "MessageStore.h" -#include "TxOp.h" +#include "qpid/broker/BrokerImportExport.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageStore.h" +#include "qpid/broker/TxOp.h" #include <algorithm> #include <functional> @@ -46,37 +47,43 @@ namespace qpid { * dispatch or to be added to the in-memory queue. */ class TxPublish : public TxOp, public Deliverable{ - class Prepare{ - TransactionContext* ctxt; - boost::intrusive_ptr<Message>& msg; - public: - Prepare(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg); - void operator()(Queue::shared_ptr& queue); - }; class Commit{ boost::intrusive_ptr<Message>& msg; public: Commit(boost::intrusive_ptr<Message>& msg); - void operator()(Queue::shared_ptr& queue); + void operator()(const boost::shared_ptr<Queue>& queue); + }; + class Rollback{ + boost::intrusive_ptr<Message>& msg; + public: + Rollback(boost::intrusive_ptr<Message>& msg); + void operator()(const boost::shared_ptr<Queue>& queue); }; boost::intrusive_ptr<Message> msg; std::list<Queue::shared_ptr> queues; + std::list<Queue::shared_ptr> prepared; + + void prepare(TransactionContext* ctxt, boost::shared_ptr<Queue>); public: - TxPublish(boost::intrusive_ptr<Message> msg); - virtual bool prepare(TransactionContext* ctxt) throw(); - virtual void commit() throw(); - virtual void rollback() throw(); + QPID_BROKER_EXTERN TxPublish(boost::intrusive_ptr<Message> msg); + QPID_BROKER_EXTERN virtual bool prepare(TransactionContext* ctxt) throw(); + QPID_BROKER_EXTERN virtual void commit() throw(); + QPID_BROKER_EXTERN virtual void rollback() throw(); virtual Message& getMessage() { return *msg; }; - virtual void deliverTo(Queue::shared_ptr& queue); + QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue); virtual ~TxPublish(){} + virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); } + + QPID_BROKER_EXTERN uint64_t contentSize(); - uint64_t contentSize(); + boost::intrusive_ptr<Message> getMessage() const { return msg; } + const std::list<Queue::shared_ptr> getQueues() const { return queues; } }; } } diff --git a/cpp/src/qpid/broker/Vhost.cpp b/cpp/src/qpid/broker/Vhost.cpp index 23203ec13e..df37cba255 100644 --- a/cpp/src/qpid/broker/Vhost.cpp +++ b/cpp/src/qpid/broker/Vhost.cpp @@ -17,23 +17,33 @@ // under the License. // -#include "Vhost.h" -#include "qpid/agent/ManagementAgent.h" +#include "qpid/broker/Vhost.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" using namespace qpid::broker; using qpid::management::ManagementAgent; +namespace _qmf = qmf::org::apache::qpid::broker; -Vhost::Vhost (management::Manageable* parentBroker) : mgmtObject(0) +namespace qpid { namespace management { +class Manageable; +}} + +Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmtObject(0) { - if (parentBroker != 0) + if (parentBroker != 0 && broker != 0) { - ManagementAgent* agent = ManagementAgent::Singleton::getInstance(); + ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtObject = new management::Vhost (agent, this, parentBroker, "/"); - agent->addObject (mgmtObject, 3, 1); + mgmtObject = new _qmf::Vhost(agent, this, parentBroker, "/"); + agent->addObject (mgmtObject, 0x1000000000000003LL); } } } +void Vhost::setFederationTag(const std::string& tag) +{ + mgmtObject->set_federationTag(tag); +} diff --git a/cpp/src/qpid/broker/Vhost.h b/cpp/src/qpid/broker/Vhost.h index e56cc61272..9554d641c2 100644 --- a/cpp/src/qpid/broker/Vhost.h +++ b/cpp/src/qpid/broker/Vhost.h @@ -21,29 +21,28 @@ // #include "qpid/management/Manageable.h" -#include "qpid/management/Vhost.h" +#include "qmf/org/apache/qpid/broker/Vhost.h" #include <boost/shared_ptr.hpp> namespace qpid { namespace broker { +class Broker; class Vhost : public management::Manageable { private: - management::Vhost* mgmtObject; + qmf::org::apache::qpid::broker::Vhost* mgmtObject; public: typedef boost::shared_ptr<Vhost> shared_ptr; - Vhost (management::Manageable* parentBroker); + Vhost (management::Manageable* parentBroker, Broker* broker = 0); management::ManagementObject* GetManagementObject (void) const { return mgmtObject; } - - management::Manageable::status_t ManagementMethod (uint32_t, management::Args&) - { return management::Manageable::STATUS_OK; } + void setFederationTag(const std::string& tag); }; }} diff --git a/cpp/src/qpid/broker/XmlExchange.cpp b/cpp/src/qpid/broker/XmlExchange.cpp deleted file mode 100644 index cb0f9a9606..0000000000 --- a/cpp/src/qpid/broker/XmlExchange.cpp +++ /dev/null @@ -1,277 +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 "config.h" -#include "XmlExchange.h" - -#include "DeliverableMessage.h" - -#include "qpid/log/Statement.h" -#include "qpid/framing/FieldTable.h" -#include "qpid/framing/FieldValue.h" -#include "qpid/framing/reply_exceptions.h" - -#include <xercesc/framework/MemBufInputSource.hpp> - -#include <xqilla/context/ItemFactory.hpp> -#include <xqilla/xqilla-simple.hpp> - -#include <iostream> -#include <sstream> - -using namespace qpid::framing; -using namespace qpid::sys; -using qpid::management::Manageable; - -namespace qpid { -namespace broker { - -XmlExchange::XmlExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) -{ - if (mgmtExchange != 0) - mgmtExchange->set_type (typeName); -} - -XmlExchange::XmlExchange(const std::string& _name, bool _durable, - const FieldTable& _args, Manageable* _parent) : - Exchange(_name, _durable, _args, _parent) -{ - if (mgmtExchange != 0) - mgmtExchange->set_type (typeName); -} - -/* - * Use the name of the query as the binding key. - * - * The first time a given name is used in a binding, the query body - * must be provided.After that, no query body should be present. - * - * To modify an installed query, the user must first unbind the - * existing query, then replace it by binding again with the same - * name. - * - */ - - // #### TODO: The Binding should take the query text - // #### only. Consider encapsulating the entire block, including - // #### the if condition. - - -bool XmlExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* bindingArguments) -{ - string queryText = bindingArguments->getString("xquery"); - - try { - RWlock::ScopedWlock l(lock); - XmlBinding::vector& bindings(bindingsMap[routingKey]); - XmlBinding::vector::iterator i; - - for (i = bindings.begin(); i != bindings.end(); i++) - if ((*i)->queue == queue) - break; - - if (i == bindings.end()) { - - Query query(xqilla.parse(X(queryText.c_str()))); - XmlBinding::shared_ptr binding(new XmlBinding (routingKey, queue, this, query)); - XmlBinding::vector bindings(1, binding); - bindingsMap[routingKey] = bindings; - QPID_LOG(trace, "Bound successfully with query: " << queryText ); - - if (mgmtExchange != 0) { - mgmtExchange->inc_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->inc_bindingCount(); - } - return true; - } else{ - return false; - } - } - catch (XQException& e) { - throw InternalErrorException(QPID_MSG("Could not parse xquery:"+ queryText)); - } - catch (...) { - throw InternalErrorException(QPID_MSG("Unexpected error - Could not parse xquery:"+ queryText)); - } -} - -bool XmlExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/) -{ - RWlock::ScopedWlock l(lock); - XmlBinding::vector& bindings(bindingsMap[routingKey]); - XmlBinding::vector::iterator i; - - for (i = bindings.begin(); i != bindings.end(); i++) - if ((*i)->queue == queue) - break; - - if (i < bindings.end()) { - bindings.erase(i); - if (bindings.empty()) { - bindingsMap.erase(routingKey); - } - if (mgmtExchange != 0) { - mgmtExchange->dec_bindingCount(); - ((management::Queue*) queue->GetManagementObject())->dec_bindingCount(); - } - return true; - } else { - return false; - } -} - -bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args) -{ - // ### TODO: Need istream for frameset - // Hack alert - the following code does not work for really large messages - - string msgContent; - - try { - msg.getMessage().getFrames().getContent(msgContent); - - QPID_LOG(trace, "matches: query is [" << UTF8(query->getQueryText()) << "]"); - QPID_LOG(trace, "matches: message content is [" << msgContent << "]"); - - boost::scoped_ptr<DynamicContext> context(query->createDynamicContext()); - if (!context.get()) { - throw InternalErrorException(QPID_MSG("Query context looks munged ...")); - } - - XERCES_CPP_NAMESPACE::MemBufInputSource xml((XMLByte*) msgContent.c_str(), msgContent.length(), "input" ); - Sequence seq(context->parseDocument(xml)); - - if (args) { - FieldTable::ValueMap::const_iterator v = args->begin(); - for(; v != args->end(); ++v) { - // ### TODO: Do types properly - if (v->second->convertsTo<std::string>()) { - QPID_LOG(trace, "XmlExchange, external variable: " << v->first << " = " << v->second->getData().getString().c_str()); - Item::Ptr value = context->getItemFactory()->createString(X(v->second->getData().getString().c_str()), context.get()); - context->setExternalVariable(X(v->first.c_str()), value); - } - } - } - - if(!seq.isEmpty() && seq.first()->isNode()) { - context->setContextItem(seq.first()); - context->setContextPosition(1); - context->setContextSize(1); - } - Result result = query->execute(context.get()); - return result->getEffectiveBooleanValue(context.get(), 0); - } - catch (XQException& e) { - QPID_LOG(warning, "Could not parse XML content (or message headers):" << msgContent); - return 0; - } - catch (...) { - QPID_LOG(warning, "Unexpected error routing message: " << msgContent); - return 0; - } - return 0; -} - -void XmlExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* args) -{ - try { - RWlock::ScopedRlock l(lock); - XmlBinding::vector& bindings(bindingsMap[routingKey]); - XmlBinding::vector::iterator i; - int count(0); - - for (i = bindings.begin(); i != bindings.end(); i++) { - - if ((*i)->xquery && matches((*i)->xquery, msg, args)) { // Overly defensive? There should always be a query ... - msg.deliverTo((*i)->queue); - count++; - QPID_LOG(trace, "Delivered to queue" ); - - if ((*i)->mgmtBinding != 0) - (*i)->mgmtBinding->inc_msgMatched (); - } - - if(!count){ - QPID_LOG(warning, "XMLExchange " << getName() << ": could not route message with query " << routingKey); - if (mgmtExchange != 0) { - mgmtExchange->inc_msgDrops (); - mgmtExchange->inc_byteDrops (msg.contentSize ()); - } - } - else { - if (mgmtExchange != 0) { - mgmtExchange->inc_msgRoutes (count); - mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); - } - } - - if (mgmtExchange != 0) { - mgmtExchange->inc_msgReceives (); - mgmtExchange->inc_byteReceives (msg.contentSize ()); - } - } - } - catch (...) { - QPID_LOG(warning, "XMLExchange " << getName() << ": exception routing message with query " << routingKey); - } - - -} - - -bool XmlExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) -{ - XmlBinding::vector::iterator j; - - if (routingKey) { - XmlBindingsMap::iterator i = bindingsMap.find(*routingKey); - - if (i == bindingsMap.end()) - return false; - if (!queue) - return true; - for (j = i->second.begin(); j != i->second.end(); j++) - if ((*j)->queue == queue) - return true; - } else if (!queue) { - //if no queue or routing key is specified, just report whether any bindings exist - return bindingsMap.size() > 0; - } else { - for (XmlBindingsMap::iterator i = bindingsMap.begin(); i != bindingsMap.end(); i++) - for (j = i->second.begin(); j != i->second.end(); j++) - if ((*j)->queue == queue) - return true; - return false; - } - - return false; -} - - -XmlExchange::~XmlExchange() -{ - bindingsMap.clear(); -} - -const std::string XmlExchange::typeName("xml"); - -} -} diff --git a/cpp/src/qpid/broker/XmlExchange.h b/cpp/src/qpid/broker/XmlExchange.h deleted file mode 100644 index 883bfceaca..0000000000 --- a/cpp/src/qpid/broker/XmlExchange.h +++ /dev/null @@ -1,87 +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 _XmlExchange_ -#define _XmlExchange_ - -#include "Exchange.h" -#include "qpid/framing/FieldTable.h" -#include "qpid/sys/Monitor.h" -#include "Queue.h" - -#include <xqilla/xqilla-simple.hpp> - -#include <boost/scoped_ptr.hpp> - -#include <map> -#include <vector> - -namespace qpid { -namespace broker { - -class XmlExchange : public virtual Exchange { - - typedef boost::shared_ptr<XQQuery> Query; - - struct XmlBinding : public Exchange::Binding { - typedef boost::shared_ptr<XmlBinding> shared_ptr; - typedef std::vector<XmlBinding::shared_ptr> vector; - - boost::shared_ptr<XQQuery> xquery; - - XmlBinding(const std::string& key, const Queue::shared_ptr queue, Exchange* parent, Query query): - Binding(key, queue, parent), xquery(query) {} - }; - - - typedef std::map<string, XmlBinding::vector > XmlBindingsMap; - - XmlBindingsMap bindingsMap; - XQilla xqilla; - qpid::sys::RWlock lock; - - bool matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args); - - public: - static const std::string typeName; - - XmlExchange(const std::string& name, management::Manageable* parent = 0); - XmlExchange(const string& _name, bool _durable, - const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); - - virtual std::string getType() const { return typeName; } - - virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); - - 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 bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); - - virtual ~XmlExchange(); -}; - - -} -} - - -#endif diff --git a/cpp/src/qpid/broker/DeliveryToken.h b/cpp/src/qpid/broker/posix/BrokerDefaults.cpp index 8bdf5e6359..9e463fa32d 100644 --- a/cpp/src/qpid/broker/DeliveryToken.h +++ b/cpp/src/qpid/broker/posix/BrokerDefaults.cpp @@ -18,28 +18,23 @@ * under the License. * */ -#ifndef _DeliveryToken_ -#define _DeliveryToken_ -#include <boost/shared_ptr.hpp> +#include "qpid/broker/Broker.h" +#include <stdlib.h> namespace qpid { namespace broker { - /** - * A DeliveryToken allows the delivery of a message to be - * associated with whatever mechanism caused it to be - * delivered. (i.e. its a form of Memento). - */ - class DeliveryToken - { - public: - typedef boost::shared_ptr<DeliveryToken> shared_ptr; +const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("/tmp"); +const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("/.qpidd"); - virtual ~DeliveryToken(){} - }; +std::string +Broker::Options::getHome() { + std::string home; + char *home_c = ::getenv("HOME"); + if (home_c != 0) + home += home_c; + return home; +} -}} - - -#endif +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/windows/BrokerDefaults.cpp b/cpp/src/qpid/broker/windows/BrokerDefaults.cpp new file mode 100644 index 0000000000..b6862f0418 --- /dev/null +++ b/cpp/src/qpid/broker/windows/BrokerDefaults.cpp @@ -0,0 +1,41 @@ +/* + * + * 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/Broker.h" +#include <stdlib.h> + +namespace qpid { +namespace broker { + +const std::string Broker::Options::DEFAULT_DATA_DIR_LOCATION("\\TEMP"); +const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("\\QPIDD.DATA"); + +std::string +Broker::Options::getHome() { + std::string home; + char home_c[MAX_PATH+1]; + size_t unused; + if (0 == getenv_s (&unused, home_c, sizeof(home_c), "HOME")) + home += home_c; + return home; +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp new file mode 100644 index 0000000000..212d7c4db4 --- /dev/null +++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp @@ -0,0 +1,190 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// This source is only used on Windows; SSPI is the Windows mechanism for +// accessing authentication mechanisms, analogous to Cyrus SASL. + +#include "qpid/broker/Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" + +#include <windows.h> + +using namespace qpid::framing; +using qpid::sys::SecurityLayer; + +namespace qpid { +namespace broker { + +class NullAuthenticator : public SaslAuthenticator +{ + Connection& connection; + framing::AMQP_ClientProxy::Connection client; +public: + NullAuthenticator(Connection& connection); + ~NullAuthenticator(); + void getMechanisms(framing::Array& mechanisms); + void start(const std::string& mechanism, const std::string& response); + void step(const std::string&) {} + std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize); +}; + +class SspiAuthenticator : public SaslAuthenticator +{ + HANDLE userToken; + Connection& connection; + framing::AMQP_ClientProxy::Connection client; + +public: + SspiAuthenticator(Connection& connection); + ~SspiAuthenticator(); + void getMechanisms(framing::Array& mechanisms); + void start(const std::string& mechanism, const std::string& response); + void step(const std::string& response); + std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize); +}; + +bool SaslAuthenticator::available(void) +{ + return true; +} + +// Initialize the SASL mechanism; throw if it fails. +void SaslAuthenticator::init(const std::string& /*saslName*/) +{ + return; +} + +void SaslAuthenticator::fini(void) +{ + return; +} + +std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c) +{ + if (c.getBroker().getOptions().auth) { + return std::auto_ptr<SaslAuthenticator>(new SspiAuthenticator(c)); + } else { + return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c)); + } +} + +NullAuthenticator::NullAuthenticator(Connection& c) : connection(c), client(c.getOutput()) {} +NullAuthenticator::~NullAuthenticator() {} + +void NullAuthenticator::getMechanisms(Array& mechanisms) +{ + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS"))); +} + +void NullAuthenticator::start(const string& mechanism, const string& response) +{ + QPID_LOG(warning, "SASL: No Authentication Performed"); + if (mechanism == "PLAIN") { // Old behavior + if (response.size() > 0 && response[0] == (char) 0) { + string temp = response.substr(1); + string::size_type i = temp.find((char)0); + string uid = temp.substr(0, i); + string pwd = temp.substr(i + 1); + connection.setUserId(uid); + } + } else { + connection.setUserId("anonymous"); + } + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); +} + +std::auto_ptr<SecurityLayer> NullAuthenticator::getSecurityLayer(uint16_t) +{ + std::auto_ptr<SecurityLayer> securityLayer; + return securityLayer; +} + + +SspiAuthenticator::SspiAuthenticator(Connection& c) : userToken(INVALID_HANDLE_VALUE), connection(c), client(c.getOutput()) +{ +} + +SspiAuthenticator::~SspiAuthenticator() +{ + if (INVALID_HANDLE_VALUE != userToken) { + CloseHandle(userToken); + userToken = INVALID_HANDLE_VALUE; + } +} + +void SspiAuthenticator::getMechanisms(Array& mechanisms) +{ + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string("ANONYMOUS")))); + mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string("PLAIN")))); + QPID_LOG(info, "SASL: Mechanism list: ANONYMOUS PLAIN"); +} + +void SspiAuthenticator::start(const string& mechanism, const string& response) +{ + QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism); + if (mechanism == "ANONYMOUS") { + connection.setUserId("anonymous"); + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); + return; + } + if (mechanism != "PLAIN") + throw ConnectionForcedException("Unsupported mechanism"); + + // PLAIN's response is composed of 3 strings separated by 0 bytes: + // authorization id, authentication id (user), clear-text password. + if (response.size() == 0) + throw ConnectionForcedException("Authentication failed"); + + string::size_type i = response.find((char)0); + string auth = response.substr(0, i); + string::size_type j = response.find((char)0, i+1); + string uid = response.substr(i+1, j-1); + string pwd = response.substr(j+1); + int error = 0; + if (!LogonUser(uid.c_str(), ".", pwd.c_str(), + LOGON32_LOGON_NETWORK, + LOGON32_PROVIDER_DEFAULT, + &userToken)) + error = GetLastError(); + pwd.replace(0, string::npos, 1, (char)0); + if (error != 0) { + QPID_LOG(info, + "SASL: Auth failed [" << error << "]: " << qpid::sys::strError(error)); + throw ConnectionForcedException("Authentication failed"); + } + + connection.setUserId(uid); + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); +} + +void SspiAuthenticator::step(const string& response) +{ + QPID_LOG(info, "SASL: Need another step!!!"); +} + +std::auto_ptr<SecurityLayer> SspiAuthenticator::getSecurityLayer(uint16_t) +{ + std::auto_ptr<SecurityLayer> securityLayer; + return securityLayer; +} + +}} |
