diff options
Diffstat (limited to 'cpp/src/qpid/broker')
116 files changed, 5181 insertions, 1883 deletions
diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h index 4caf8ed3ce..c01697ace9 100644 --- a/cpp/src/qpid/broker/AclModule.h +++ b/cpp/src/qpid/broker/AclModule.h @@ -79,6 +79,8 @@ namespace acl { PROP_POLICYTYPE, PROP_MAXQUEUESIZE, PROP_MAXQUEUECOUNT, + PROP_MAXFILESIZE, + PROP_MAXFILECOUNT, PROPERTYSIZE // PROPERTYSIZE must be last in list }; @@ -102,7 +104,11 @@ namespace acl { SPECPROP_MAXQUEUESIZELOWERLIMIT, SPECPROP_MAXQUEUESIZEUPPERLIMIT, SPECPROP_MAXQUEUECOUNTLOWERLIMIT, - SPECPROP_MAXQUEUECOUNTUPPERLIMIT }; + SPECPROP_MAXQUEUECOUNTUPPERLIMIT, + SPECPROP_MAXFILESIZELOWERLIMIT, + SPECPROP_MAXFILESIZEUPPERLIMIT, + SPECPROP_MAXFILECOUNTLOWERLIMIT, + SPECPROP_MAXFILECOUNTUPPERLIMIT }; // AclResult shared between ACL spec and ACL authorise interface enum AclResult { @@ -126,6 +132,8 @@ namespace broker { // doTransferAcl pervents time consuming ACL calls on a per-message basis. virtual bool doTransferAcl()=0; + virtual uint16_t getMaxConnectTotal()=0; + virtual bool authorise( const std::string& id, const acl::Action& action, @@ -147,9 +155,10 @@ namespace broker { */ virtual bool approveConnection (const Connection& connection)=0; - /** Change connection's counted userId + /** Approve queue creation by counting per-user. */ - virtual void setUserId(const Connection& connection, const std::string& username)=0; + virtual bool approveCreateQueue(const std::string& userId, const std::string& queueName)=0; + virtual void recordDestroyQueue(const std::string& queueName)=0; virtual ~AclModule() {}; }; @@ -222,6 +231,8 @@ namespace acl { if (str.compare("policytype") == 0) return PROP_POLICYTYPE; if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE; if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT; + if (str.compare("maxfilesize") == 0) return PROP_MAXFILESIZE; + if (str.compare("maxfilecount") == 0) return PROP_MAXFILECOUNT; throw qpid::Exception(str); } static inline std::string getPropertyStr(const Property p) { @@ -240,6 +251,8 @@ namespace acl { case PROP_POLICYTYPE: return "policytype"; case PROP_MAXQUEUESIZE: return "maxqueuesize"; case PROP_MAXQUEUECOUNT: return "maxqueuecount"; + case PROP_MAXFILESIZE: return "maxfilesize"; + case PROP_MAXFILECOUNT: return "maxfilecount"; default: assert(false); // should never get here } return ""; @@ -261,6 +274,10 @@ namespace acl { if (str.compare("queuemaxsizeupperlimit") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT; if (str.compare("queuemaxcountlowerlimit") == 0) return SPECPROP_MAXQUEUECOUNTLOWERLIMIT; if (str.compare("queuemaxcountupperlimit") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT; + if (str.compare("filemaxsizelowerlimit") == 0) return SPECPROP_MAXFILESIZELOWERLIMIT; + if (str.compare("filemaxsizeupperlimit") == 0) return SPECPROP_MAXFILESIZEUPPERLIMIT; + if (str.compare("filemaxcountlowerlimit") == 0) return SPECPROP_MAXFILECOUNTLOWERLIMIT; + if (str.compare("filemaxcountupperlimit") == 0) return SPECPROP_MAXFILECOUNTUPPERLIMIT; // Allow old names in ACL file as aliases for newly-named properties if (str.compare("maxqueuesize") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT; if (str.compare("maxqueuecount") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT; @@ -284,6 +301,10 @@ namespace acl { case SPECPROP_MAXQUEUESIZEUPPERLIMIT: return "queuemaxsizeupperlimit"; case SPECPROP_MAXQUEUECOUNTLOWERLIMIT: return "queuemaxcountlowerlimit"; case SPECPROP_MAXQUEUECOUNTUPPERLIMIT: return "queuemaxcountupperlimit"; + case SPECPROP_MAXFILESIZELOWERLIMIT: return "filemaxsizelowerlimit"; + case SPECPROP_MAXFILESIZEUPPERLIMIT: return "filemaxsizeupperlimit"; + case SPECPROP_MAXFILECOUNTLOWERLIMIT: return "filemaxcountlowerlimit"; + case SPECPROP_MAXFILECOUNTUPPERLIMIT: return "filemaxcountupperlimit"; default: assert(false); // should never get here } return ""; diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp index 4604ac643f..75aba70ae8 100644 --- a/cpp/src/qpid/broker/Bridge.cpp +++ b/cpp/src/qpid/broker/Bridge.cpp @@ -19,6 +19,8 @@ * */ #include "qpid/broker/Bridge.h" + +#include "qpid/broker/Broker.h" #include "qpid/broker/FedOps.h" #include "qpid/broker/ConnectionState.h" #include "qpid/broker/Connection.h" @@ -49,6 +51,11 @@ using qpid::management::ManagementAgent; using std::string; namespace _qmf = qmf::org::apache::qpid::broker; +namespace { +const std::string QPID_REPLICATE("qpid.replicate"); +const std::string NONE("none"); +} + namespace qpid { namespace broker { @@ -60,7 +67,7 @@ void Bridge::PushHandler::handle(framing::AMQFrame& frame) Bridge::Bridge(const std::string& _name, Link* _link, framing::ChannelId _id, CancellationListener l, const _qmf::ArgsLinkBridge& _args, InitializeCallback init, const std::string& _queueName, const string& ae) : - link(_link), channel(_id), args(_args), mgmtObject(0), + link(_link), channel(_id), args(_args), listener(l), name(_name), queueName(_queueName.empty() ? "qpid.bridge_queue_" + name + "_" + link->getBroker()->getFederationTag() : _queueName), @@ -71,10 +78,10 @@ Bridge::Bridge(const std::string& _name, Link* _link, framing::ChannelId _id, { ManagementAgent* agent = link->getBroker()->getManagementAgent(); if (agent != 0) { - mgmtObject = new _qmf::Bridge + mgmtObject = _qmf::Bridge::shared_ptr(new _qmf::Bridge (agent, this, link, name, 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); + args.i_tag, args.i_excludes, args.i_dynamic, args.i_sync)); mgmtObject->set_channelId(channel); agent->addObject(mgmtObject); } @@ -121,28 +128,30 @@ void Bridge::create(Connection& c, AsyncStore* const store) peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); QPID_LOG(debug, "Activated bridge " << name << " for route from queue " << args.i_src << " to " << args.i_dest); } else { - FieldTable queueSettings; - - if (args.i_tag.size()) { - queueSettings.setString("qpid.trace.id", args.i_tag); - } else { - const string& peerTag = c.getFederationPeerTag(); - if (peerTag.size()) - queueSettings.setString("qpid.trace.id", peerTag); + if (!useExistingQueue) { + FieldTable queueSettings; + + if (args.i_tag.size()) { + queueSettings.setString("qpid.trace.id", args.i_tag); + } else { + 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 exclusive = true; // only exclusive if the queue is owned by the bridge + bool autoDelete = exclusive && !durable;//auto delete transient queues? + peer->getQueue().declare(queueName, altEx, false, durable, exclusive, autoDelete, queueSettings); } - - 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 exclusive = !useExistingQueue; // only exclusive if the queue is owned by the bridge - bool autoDelete = exclusive && !durable;//auto delete transient queues? - peer->getQueue().declare(queueName, altEx, false, durable, exclusive, autoDelete, queueSettings); if (!args.i_dynamic) peer->getExchange().bind(queueName, args.i_src, args.i_key, FieldTable()); peer->getMessage().subscribe(queueName, args.i_dest, (useExistingQueue && args.i_sync) ? 0 : 1, 0, false, "", 0, options); @@ -296,9 +305,9 @@ uint32_t Bridge::encodedSize() const + 2; // sync } -management::ManagementObject* Bridge::GetManagementObject (void) const +management::ManagementObject::shared_ptr Bridge::GetManagementObject(void) const { - return (management::ManagementObject*) mgmtObject; + return mgmtObject; } management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, @@ -331,6 +340,7 @@ void Bridge::propagateBinding(const string& key, const string& tagList, } string newTagList(tagList + string(tagList.empty() ? "" : ",") + localTag); + bindArgs.setString(QPID_REPLICATE, NONE); bindArgs.setString(qpidFedOp, op); bindArgs.setString(qpidFedTags, newTagList); if (origin.empty()) @@ -366,8 +376,7 @@ void Bridge::ioThreadPropagateBinding(const string& queue, const string& exchang if (resetProxy()) { peer->getExchange().bind(queue, exchange, key, args); } else { - QPID_LOG(error, "Cannot propagate binding for dynamic bridge as session has been detached, deleting dynamic bridge"); - close(); + // link's periodic maintenance visit will attempt to recover } } diff --git a/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h index 04ac585d80..893febdf92 100644 --- a/cpp/src/qpid/broker/Bridge.h +++ b/cpp/src/qpid/broker/Bridge.h @@ -65,6 +65,7 @@ class Bridge : public PersistableConfig, QPID_BROKER_EXTERN void close(); bool isDurable() { return args.i_durable; } + framing::ChannelId getChannel() const { return channel; } Link *getLink() const { return link; } const std::string getSrc() const { return args.i_src; } const std::string getDest() const { return args.i_dest; } @@ -72,7 +73,7 @@ class Bridge : public PersistableConfig, bool isDetached() const { return detached; } - management::ManagementObject* GetManagementObject() const; + management::ManagementObject::shared_ptr GetManagementObject() const; management::Manageable::status_t ManagementMethod(uint32_t methodId, management::Args& args, std::string& text); @@ -128,7 +129,7 @@ class Bridge : public PersistableConfig, Link* const link; const framing::ChannelId channel; qmf::org::apache::qpid::broker::ArgsLinkBridge args; - qmf::org::apache::qpid::broker::Bridge* mgmtObject; + qmf::org::apache::qpid::broker::Bridge::shared_ptr mgmtObject; CancellationListener listener; std::string name; std::string queueName; diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp index 08606516d4..49e80689fc 100644 --- a/cpp/src/qpid/broker/Broker.cpp +++ b/cpp/src/qpid/broker/Broker.cpp @@ -20,6 +20,8 @@ */ #include "qpid/broker/Broker.h" + +#include "qpid/broker/AclModule.h" #include "qpid/broker/AsyncResultHandle.h" #include "qpid/broker/ConfigAsyncContext.h" #include "qpid/broker/ConfigHandle.h" @@ -27,9 +29,7 @@ #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/RecoveryAsyncContext.h" +#include "qpid/broker/NameGenerator.h" #include "qpid/broker/RecoveryManagerImpl.h" #include "qpid/broker/SaslAuthenticator.h" #include "qpid/broker/SecureConnectionFactory.h" @@ -43,6 +43,7 @@ #include "qpid/broker/MessageGroupManager.h" #include "qmf/org/apache/qpid/broker/Package.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerConnect.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerCreate.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerDelete.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerQuery.h" @@ -50,12 +51,12 @@ #include "qmf/org/apache/qpid/broker/ArgsBrokerGetLogLevel.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerSetLogLevel.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerGetLogHiresTimestamp.h" +#include "qmf/org/apache/qpid/broker/ArgsBrokerSetLogHiresTimestamp.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerSetTimestampConfig.h" #include "qmf/org/apache/qpid/broker/ArgsBrokerGetTimestampConfig.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 "qpid/amqp_0_10/Codecs.h" @@ -75,6 +76,7 @@ #include "qpid/sys/Dispatcher.h" #include "qpid/sys/Thread.h" #include "qpid/sys/Time.h" +#include "qpid/sys/Timer.h" #include "qpid/sys/ConnectionInputHandler.h" #include "qpid/sys/ConnectionInputHandlerFactory.h" #include "qpid/sys/TimeoutHandler.h" @@ -110,6 +112,17 @@ namespace _qmf = qmf::org::apache::qpid::broker; namespace qpid { namespace broker { +const std::string empty; +const std::string amq_direct("amq.direct"); +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"); + +// static +Broker* Broker::thisBroker; + Broker::Options::Options(const std::string& name) : qpid::Options(name), noDataDir(0), @@ -127,6 +140,7 @@ Broker::Options::Options(const std::string& name) : queueLimit(100*1048576/*100M default limit*/), tcpNoDelay(false), requireEncrypted(false), + knownHosts(knownHostsNone), qmf2Support(true), qmf1Support(true), queueFlowStopRatio(80), @@ -136,7 +150,7 @@ Broker::Options::Options(const std::string& name) : timestampRcvMsgs(false), // set the 0.10 timestamp delivery property linkMaintenanceInterval(2), linkHeartbeatInterval(120), - maxNegotiateTime(2000) // 2s + maxNegotiateTime(10000) // 10s { int c = sys::SystemInfo::concurrency(); workerThreads=c+1; @@ -152,6 +166,7 @@ Broker::Options::Options(const std::string& name) : ("data-dir", optValue(dataDir,"DIR"), "Directory to contain persistent data generated by the broker") ("no-data-dir", optValue(noDataDir), "Don't use a data directory. No persistent configuration will be loaded or stored") ("port,p", optValue(port,"PORT"), "Tells the broker to listen on PORT") + ("interface", optValue(listenInterfaces, "<interface name>|<interface address>"), "Which network interfaces to use to listen for incoming connections") ("worker-threads", optValue(workerThreads, "N"), "Sets the broker thread pool size") ("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket") ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management") @@ -175,23 +190,27 @@ Broker::Options::Options(const std::string& name) : ("default-event-threshold-ratio", optValue(queueThresholdEventRatio, "%age of limit"), "The ratio of any specified queue limit at which an event will be raised") ("default-message-group", optValue(defaultMsgGroup, "GROUP-IDENTIFER"), "Group identifier to assign to messages delivered to a message group queue that do not contain an identifier.") ("enable-timestamp", optValue(timestampRcvMsgs, "yes|no"), "Add current time to each received message.") - ("link-maintenace-interval", optValue(linkMaintenanceInterval, "SECONDS")) - ("link-heartbeat-interval", optValue(linkHeartbeatInterval, "SECONDS")) - ("max-negotiate-time", optValue(maxNegotiateTime, "MilliSeconds"), "Maximum time a connection can take to send the initial protocol negotiation") + ("link-maintenance-interval", optValue(linkMaintenanceInterval, "SECONDS"), + "Interval to check link health and re-connect if need be") + ("link-heartbeat-interval", optValue(linkHeartbeatInterval, "SECONDS"), + "Heartbeat interval for a federation link") + ("max-negotiate-time", optValue(maxNegotiateTime, "MILLISECONDS"), "Maximum time a connection can take to send the initial protocol negotiation") ("federation-tag", optValue(fedTag, "NAME"), "Override the federation tag") ; } -const std::string empty; -const std::string amq_direct("amq.direct"); -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"); +namespace { +// Arguments to declare a non-replicated exchange. +framing::FieldTable noReplicateArgs() { + framing::FieldTable args; + args.setString("qpid.replicate", "none"); + return args; +} +} Broker::Broker(const Broker::Options& conf) : poller(new Poller), + timer(new qpid::sys::Timer), config(conf), managementAgent(conf.enableMgmt ? new ManagementAgent(conf.qmf1Support, conf.qmf2Support) @@ -205,21 +224,18 @@ Broker::Broker(const Broker::Options& conf) : exchanges(this), links(this), factory(new SecureConnectionFactory(*this)), - dtxManager(timer), + dtxManager(*timer.get()), sessionManager( qpid::SessionState::Configuration( conf.replayFlushLimit*1024, // convert kb to bytes. conf.replayHardLimit*1024), *this), - mgmtObject(0), - queueCleaner(queues, &timer), - recovery(true), - inCluster(false), - clusterUpdatee(false), + queueCleaner(queues, timer.get()), + recoveryInProgress(false), expiryPolicy(new ExpiryPolicy), - getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)), - deferDelivery(boost::bind(&Broker::deferDeliveryImpl, this, _1, _2)) + getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)) { + thisBroker = this; try { if (conf.enableMgmt) { QPID_LOG(info, "Management enabled"); @@ -231,7 +247,7 @@ Broker::Broker(const Broker::Options& conf) : System* system = new System (dataDir.isEnabled() ? dataDir.getPath() : string(), this); systemObject = System::shared_ptr(system); - mgmtObject = new _qmf::Broker(managementAgent.get(), this, system, "amqp-broker"); + mgmtObject = _qmf::Broker::shared_ptr(new _qmf::Broker(managementAgent.get(), this, system, "amqp-broker")); mgmtObject->set_systemRef(system->GetManagementObject()->getObjectId()); mgmtObject->set_port(conf.port); mgmtObject->set_workerThreads(conf.workerThreads); @@ -281,28 +297,26 @@ Broker::Broker(const Broker::Options& conf) : // if (NullMessageStore::isNullStore(store.get())) // setStore(); - exchanges.declare(empty, DirectExchange::typeName); // Default exchange. + framing::FieldTable args; + + // Default exchnge is not replicated. + exchanges.declare(empty, DirectExchange::typeName, false, noReplicateArgs()); // if (store.get() != 0) { if (asyncStore.get() != 0) { - // The cluster plug-in will setRecovery(false) on all but the first - // broker to join a cluster. - if (getRecovery()) { QPID_LOG(info, "Store recovery starting") - RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager); + RecoveryManagerImpl recoverer(queues, exchanges, links, dtxManager, protocolRegistry); RecoveryHandle rh = asyncStore->createRecoveryHandle(); - boost::shared_ptr<RecoveryAsyncContext> rac(new RecoveryAsyncContext(recoverer, &recoverComplete, &asyncResultQueue)); + boost::shared_ptr<RecoveryAsyncContext> rac(new RecoveryAsyncContext(recoverer, &recoverCompleteCb, &asyncResultQueue)); asyncStore->submitRecover(rh, rac); -// 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 - asyncStore->initialize(true, true); - } +// RecoveryManagerImpl recoverer( +// queues, exchanges, links, dtxManager, protocolRegistry); +// recoveryInProgress = true; +// store->recover(recoverer); +// recoveryInProgress = false; } // debug - else QPID_LOG(info, ">>>> No store!!!!") +// else QPID_LOG(info, ">>>> No store!!!!") //ensure standard exchanges exist (done after recovery from store) declareStandardExchange(amq_direct, DirectExchange::typeName); @@ -311,7 +325,7 @@ Broker::Broker(const Broker::Options& conf) : declareStandardExchange(amq_match, HeadersExchange::typeName); if(conf.enableMgmt) { - exchanges.declare(qpid_management, ManagementTopicExchange::typeName); + exchanges.declare(qpid_management, ManagementTopicExchange::typeName, false, noReplicateArgs()); Exchange::shared_ptr mExchange = exchanges.get(qpid_management); Exchange::shared_ptr dExchange = exchanges.get(amq_direct); managementAgent->setExchange(mExchange, dExchange); @@ -320,8 +334,10 @@ Broker::Broker(const Broker::Options& conf) : std::string qmfTopic("qmf.default.topic"); std::string qmfDirect("qmf.default.direct"); - std::pair<Exchange::shared_ptr, bool> topicPair(exchanges.declare(qmfTopic, ManagementTopicExchange::typeName)); - std::pair<Exchange::shared_ptr, bool> directPair(exchanges.declare(qmfDirect, ManagementDirectExchange::typeName)); + std::pair<Exchange::shared_ptr, bool> topicPair( + exchanges.declare(qmfTopic, ManagementTopicExchange::typeName, false, noReplicateArgs())); + std::pair<Exchange::shared_ptr, bool> directPair( + exchanges.declare(qmfDirect, ManagementDirectExchange::typeName, false, noReplicateArgs())); boost::dynamic_pointer_cast<ManagementDirectExchange>(directPair.first)->setManagmentAgent(managementAgent.get(), 2); boost::dynamic_pointer_cast<ManagementTopicExchange>(topicPair.first)->setManagmentAgent(managementAgent.get(), 2); @@ -349,19 +365,17 @@ Broker::Broker(const Broker::Options& conf) : // Initialize plugins Plugin::initializeAll(*this); - if (managementAgent.get()) managementAgent->pluginsInitialized(); + if(conf.enableMgmt) { + if (getAcl()) { + mgmtObject->set_maxConns(getAcl()->getMaxConnectTotal()); + } + } 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) { + if (!conf.knownHosts.empty() && conf.knownHosts != knownHostsNone) { knownBrokers.push_back(Url(conf.knownHosts)); } @@ -375,11 +389,14 @@ void Broker::declareStandardExchange(const std::string& name, const std::string& { // bool storeEnabled = store.get() != NULL; bool storeEnabled = asyncStore.get() != NULL; - std::pair<Exchange::shared_ptr, bool> status = exchanges.declare(name, type, storeEnabled); + framing::FieldTable args; + // Standard exchanges are not replicated. + std::pair<Exchange::shared_ptr, bool> status = + exchanges.declare(name, type, storeEnabled, noReplicateArgs()); if (status.second && storeEnabled) { // store->create(*status.first, framing::FieldTable ()); ConfigHandle ch = asyncStore->createConfigHandle(); - boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureComplete, &asyncResultQueue)); + boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureCompleteCb, &asyncResultQueue)); asyncStore->submitCreate(ch, status.first.get(), bc); } } @@ -420,11 +437,20 @@ void Broker::setStore () { } // static +void Broker::recoverCompleteCb(const AsyncResultHandle* const arh) { + thisBroker->recoverComplete(arh); +} + void Broker::recoverComplete(const AsyncResultHandle* const arh) { + recoveryInProgress = false; std::cout << "@@@@ Broker: Recover complete: err=" << arh->getErrNo() << "; msg=\"" << arh->getErrMsg() << "\"" << std::endl; } // static +void Broker::configureCompleteCb(const AsyncResultHandle* const arh) { + thisBroker->configureComplete(arh); +} + void Broker::configureComplete(const AsyncResultHandle* const arh) { std::cout << "@@@@ Broker: Configure complete: err=" << arh->getErrNo() << "; msg=\"" << arh->getErrMsg() << "\"" << std::endl; } @@ -464,13 +490,13 @@ Broker::~Broker() { finalize(); // Finalize any plugins. if (config.auth) SaslAuthenticator::fini(); - timer.stop(); + timer->stop(); QPID_LOG(notice, "Shut down"); } -ManagementObject* Broker::GetManagementObject(void) const +ManagementObject::shared_ptr Broker::GetManagementObject(void) const { - return (ManagementObject*) mgmtObject; + return mgmtObject; } Manageable* Broker::GetVhostObject(void) const @@ -536,7 +562,7 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId, _qmf::ArgsBrokerQueueMoveMessages& moveArgs= dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args); QPID_LOG (debug, "Broker::queueMoveMessages()"); - if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty, moveArgs.i_filter)) + if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty, moveArgs.i_filter) >= 0) status = Manageable::STATUS_OK; else return Manageable::STATUS_PARAMETER_INVALID; @@ -584,7 +610,22 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId, status = setTimestampConfig(a.i_receive, getManagementExecutionContext()); break; } - default: + + case _qmf::Broker::METHOD_GETLOGHIRESTIMESTAMP: + { + dynamic_cast<_qmf::ArgsBrokerGetLogHiresTimestamp&>(args).o_logHires = getLogHiresTimestamp(); + QPID_LOG (debug, "Broker::getLogHiresTimestamp()"); + status = Manageable::STATUS_OK; + break; + } + case _qmf::Broker::METHOD_SETLOGHIRESTIMESTAMP: + { + setLogHiresTimestamp(dynamic_cast<_qmf::ArgsBrokerSetLogHiresTimestamp&>(args).i_logHires); + QPID_LOG (debug, "Broker::setLogHiresTimestamp()"); + status = Manageable::STATUS_OK; + break; + } + default: QPID_LOG (debug, "Broker ManagementMethod not implemented: id=" << methodId << "]"); status = Manageable::STATUS_NOT_IMPLEMENTED; break; @@ -750,7 +791,7 @@ void Broker::createObject(const std::string& type, const std::string& name, else extensions[i->first] = i->second; } framing::FieldTable arguments; - amqp_0_10::translate(extensions, arguments); + qpid::amqp_0_10::translate(extensions, arguments); try { std::pair<boost::shared_ptr<Exchange>, bool> result = @@ -772,7 +813,7 @@ void Broker::createObject(const std::string& type, const std::string& name, else extensions[i->first] = i->second; } framing::FieldTable arguments; - amqp_0_10::translate(extensions, arguments); + qpid::amqp_0_10::translate(extensions, arguments); bind(binding.queue, binding.exchange, binding.key, arguments, userId, connectionId); @@ -1008,6 +1049,18 @@ std::string Broker::getLogLevel() return level; } +void Broker::setLogHiresTimestamp(bool enabled) +{ + QPID_LOG(notice, "Changing log hires timestamp to " << enabled); + qpid::log::Logger::instance().setHiresTimestamp(enabled); +} + +bool Broker::getLogHiresTimestamp() +{ + return qpid::log::Logger::instance().getHiresTimestamp(); +} + + boost::shared_ptr<ProtocolFactory> Broker::getProtocolFactory(const std::string& name) const { ProtocolFactoryMap::const_iterator i = name.empty() ? protocolFactories.begin() : protocolFactories.find(name); @@ -1036,39 +1089,29 @@ void Broker::accept() { } void Broker::connect( + const std::string& name, const std::string& host, const std::string& port, const std::string& transport, - boost::function2<void, int, std::string> failed, - sys::ConnectionCodec::Factory* f) + boost::function2<void, int, std::string> failed) { boost::shared_ptr<ProtocolFactory> pf = getProtocolFactory(transport); - if (pf) pf->connect(poller, host, port, f ? f : factory.get(), failed); + if (pf) pf->connect(poller, name, host, port, factory.get(), failed); else throw NoSuchTransportException(QPID_MSG("Unsupported transport type: " << transport)); } -void Broker::connect( - const Url& url, - boost::function2<void, int, std::string> failed, - sys::ConnectionCodec::Factory* f) -{ - url.throwIfEmpty(); - const Address& addr=url[0]; - connect(addr.host, boost::lexical_cast<std::string>(addr.port), addr.protocol, failed, f); -} - -uint32_t Broker::queueMoveMessages( +int32_t Broker::queueMoveMessages( const std::string& srcQueue, const std::string& destQueue, uint32_t qty, const Variant::Map& filter) { - 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, &filter); + Queue::shared_ptr src_queue = queues.find(srcQueue); + if (!src_queue) + return -1; + Queue::shared_ptr dest_queue = queues.find(destQueue); + if (!dest_queue) + return -1; + + return (int32_t) src_queue->move(dest_queue, qty, &filter); } @@ -1083,12 +1126,6 @@ Broker::getKnownBrokersImpl() bool Broker::deferDeliveryImpl(const std::string&, const Message&) { return false; } -void Broker::setClusterTimer(std::auto_ptr<sys::Timer> t) { - clusterTimer = t; - queueCleaner.setTimer(clusterTimer.get()); - dtxManager.setTimer(*clusterTimer.get()); -} - const std::string Broker::TCP_TRANSPORT("tcp"); @@ -1109,9 +1146,14 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( params.insert(make_pair(acl::PROP_POLICYTYPE, settings.dropMessagesAtLimit ? "ring" : "reject")); params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(settings.maxDepth.getCount()))); params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(settings.maxDepth.getSize()))); + params.insert(make_pair(acl::PROP_MAXFILECOUNT, boost::lexical_cast<string>(settings.maxFileCount))); + params.insert(make_pair(acl::PROP_MAXFILESIZE, boost::lexical_cast<string>(settings.maxFileSize))); if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_QUEUE,name,¶ms) ) throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId)); + + if (!acl->approveCreateQueue(userId,name) ) + throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId)); } Exchange::shared_ptr alternate; @@ -1120,21 +1162,12 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange)); } - std::pair<Queue::shared_ptr, bool> result = queues.declare(name, settings, alternate); + std::pair<Queue::shared_ptr, bool> result = + queues.declare(name, settings, alternate, false/*recovering*/, + owner, connectionId, userId); if (result.second) { //add default binding: result.first->bind(exchanges.getDefault(), name, qpid::framing::FieldTable()); - - if (managementAgent.get()) { - //TODO: debatable whether we should raise an event here for - //create when this is a 'declare' event; ideally add a create - //event instead? - managementAgent->raiseEvent( - _qmf::EventQueueDeclare(connectionId, userId, name, - settings.durable, owner, settings.autodelete, alternateExchange, - settings.asMap(), - "created")); - } QPID_LOG_CAT(debug, model, "Create queue. name:" << name << " user:" << userId << " rhost:" << connectionId @@ -1149,6 +1182,10 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( void Broker::deleteQueue(const std::string& name, const std::string& userId, const std::string& connectionId, QueueFunctor check) { + QPID_LOG_CAT(debug, model, "Deleting queue. name:" << name + << " user:" << userId + << " rhost:" << connectionId + ); if (acl && !acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_QUEUE,name,NULL)) { throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue delete request from " << userId)); } @@ -1156,19 +1193,13 @@ void Broker::deleteQueue(const std::string& name, const std::string& userId, Queue::shared_ptr queue = queues.find(name); if (queue) { if (check) check(queue); - queues.destroy(name); + if (acl) + acl->recordDestroyQueue(name); + queues.destroy(name, connectionId, userId); queue->destroyed(); } else { throw framing::NotFoundException(QPID_MSG("Delete failed. No such queue: " << name)); } - - if (managementAgent.get()) - managementAgent->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, name)); - QPID_LOG_CAT(debug, model, "Delete queue. name:" << name - << " user:" << userId - << " rhost:" << connectionId - ); - } std::pair<Exchange::shared_ptr, bool> Broker::createExchange( @@ -1196,33 +1227,16 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange( } std::pair<Exchange::shared_ptr, bool> result; - result = exchanges.declare(name, type, durable, arguments); + result = exchanges.declare( + name, type, durable, arguments, alternate, connectionId, userId); if (result.second) { - if (alternate) { - result.first->setAlternate(alternate); - alternate->incAlternateUsers(); - } if (durable) { // store->create(*result.first, arguments); ConfigHandle ch = asyncStore->createConfigHandle(); result.first->setHandle(ch); - boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureComplete, &asyncResultQueue)); + boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureCompleteCb, &asyncResultQueue)); asyncStore->submitCreate(ch, result.first.get(), bc); } - if (managementAgent.get()) { - //TODO: debatable whether we should raise an event here for - //create when this is a 'declare' event; ideally add a create - //event instead? - managementAgent->raiseEvent(_qmf::EventExchangeDeclare(connectionId, - userId, - name, - type, - alternateExchange, - durable, - false, - ManagementAgent::toMap(arguments), - "created")); - } QPID_LOG_CAT(debug, model, "Create exchange. name:" << name << " user:" << userId << " rhost:" << connectionId @@ -1236,6 +1250,9 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange( void Broker::deleteExchange(const std::string& name, const std::string& userId, const std::string& connectionId) { + QPID_LOG_CAT(debug, model, "Deleting exchange. name:" << name + << " user:" << userId + << " rhost:" << connectionId); if (acl) { if (!acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) ) throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange delete request from " << userId)); @@ -1246,21 +1263,15 @@ void Broker::deleteExchange(const std::string& name, const std::string& userId, } Exchange::shared_ptr exchange(exchanges.get(name)); if (!exchange) throw framing::NotFoundException(QPID_MSG("Delete failed. No such exchange: " << name)); - if (exchange->inUseAsAlternate()) throw framing::NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange.")); + if (exchange->inUseAsAlternate()) throw framing::NotAllowedException(QPID_MSG("Cannot delete " << name <<", in use as alternate-exchange.")); // if (exchange->isDurable()) store->destroy(*exchange); if (exchange->isDurable()) { - boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureComplete, &asyncResultQueue)); + boost::shared_ptr<BrokerAsyncContext> bc(new ConfigAsyncContext(&configureCompleteCb, &asyncResultQueue)); asyncStore->submitDestroy(exchange->getHandle(), bc); exchange->resetHandle(); } if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers(); - exchanges.destroy(name); - - if (managementAgent.get()) - managementAgent->raiseEvent(_qmf::EventExchangeDelete(connectionId, userId, name)); - QPID_LOG_CAT(debug, model, "Delete exchange. name:" << name - << " user:" << userId - << " rhost:" << connectionId); + exchanges.destroy(name, connectionId, userId); } void Broker::bind(const std::string& queueName, @@ -1298,6 +1309,7 @@ void Broker::bind(const std::string& queueName, QPID_LOG_CAT(debug, model, "Create binding. exchange:" << exchangeName << " queue:" << queueName << " key:" << key + << " arguments:" << arguments << " user:" << userId << " rhost:" << connectionId); } diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h index 698d446bca..468da8983a 100644 --- a/cpp/src/qpid/broker/Broker.h +++ b/cpp/src/qpid/broker/Broker.h @@ -25,40 +25,30 @@ #include "qpid/broker/AsyncResultQueueImpl.h" #include "qpid/broker/AsyncStore.h" #include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/ConnectionFactory.h" -#include "qpid/broker/ConnectionToken.h" -#include "qpid/broker/DirectExchange.h" + +#include "qpid/DataDir.h" +#include "qpid/Options.h" +#include "qpid/Plugin.h" #include "qpid/broker/DtxManager.h" #include "qpid/broker/ExchangeRegistry.h" //#include "qpid/broker/MessageStore.h" +#include "qpid/broker/Protocol.h" #include "qpid/broker/QueueRegistry.h" #include "qpid/broker/LinkRegistry.h" #include "qpid/broker/SessionManager.h" #include "qpid/broker/QueueCleaner.h" #include "qpid/broker/Vhost.h" #include "qpid/broker/System.h" -#include "qpid/broker/ExpiryPolicy.h" #include "qpid/broker/ConsumerFactory.h" #include "qpid/broker/ConnectionObservers.h" #include "qpid/broker/ConfigurationObservers.h" #include "qpid/management/Manageable.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" -#include "qpid/framing/FrameHandler.h" -#include "qpid/framing/OutputHandler.h" -#include "qpid/framing/ProtocolInitiation.h" -#include "qpid/sys/Runnable.h" -#include "qpid/sys/Timer.h" -#include "qpid/types/Variant.h" -#include "qpid/RefCounted.h" -#include "qpid/broker/AclModule.h" +#include "qpid/sys/ConnectionCodec.h" #include "qpid/sys/Mutex.h" +#include "qpid/sys/Runnable.h" #include <boost/intrusive_ptr.hpp> + #include <string> #include <vector> @@ -67,12 +57,14 @@ namespace qpid { namespace sys { class ProtocolFactory; class Poller; +class Timer; } struct Url; namespace broker { +class AclModule; class ConnectionState; class ExpiryPolicy; class Message; @@ -103,6 +95,7 @@ class Broker : public sys::Runnable, public Plugin::Target, bool noDataDir; std::string dataDir; uint16_t port; + std::vector<std::string> listenInterfaces; int workerThreads; int connectionBacklog; bool enableMgmt; @@ -139,10 +132,15 @@ class Broker : public sys::Runnable, public Plugin::Target, void declareStandardExchange(const std::string& name, const std::string& type); void setStore (); - static void recoverComplete(const AsyncResultHandle* const); - static void configureComplete(const AsyncResultHandle* const); + static void recoverCompleteCb(const AsyncResultHandle* const); + static void configureCompleteCb(const AsyncResultHandle* const); + static Broker* thisBroker; + void recoverComplete(const AsyncResultHandle* const arh); + void configureComplete(const AsyncResultHandle* const arh); void setLogLevel(const std::string& level); std::string getLogLevel(); + void setLogHiresTimestamp(bool enabled); + bool getLogHiresTimestamp(); void createObject(const std::string& type, const std::string& name, const qpid::types::Variant::Map& properties, bool strict, const ConnectionState* context); void deleteObject(const std::string& type, const std::string& name, @@ -158,8 +156,7 @@ class Broker : public sys::Runnable, public Plugin::Target, Manageable::status_t setTimestampConfig(const bool receive, const ConnectionState* context); boost::shared_ptr<sys::Poller> poller; - sys::Timer timer; - std::auto_ptr<sys::Timer> clusterTimer; + std::auto_ptr<sys::Timer> timer; Options config; std::auto_ptr<management::ManagementAgent> managementAgent; ProtocolFactoryMap protocolFactories; @@ -178,7 +175,7 @@ class Broker : public sys::Runnable, public Plugin::Target, boost::shared_ptr<sys::ConnectionCodec::Factory> factory; DtxManager dtxManager; SessionManager sessionManager; - qmf::org::apache::qpid::broker::Broker* mgmtObject; + qmf::org::apache::qpid::broker::Broker::shared_ptr mgmtObject; Vhost::shared_ptr vhostObject; System::shared_ptr systemObject; QueueCleaner queueCleaner; @@ -187,10 +184,10 @@ class Broker : public sys::Runnable, public Plugin::Target, bool deferDeliveryImpl(const std::string& queue, const Message& msg); std::string federationTag; - bool recovery; - bool inCluster, clusterUpdatee; + bool recoveryInProgress; boost::intrusive_ptr<ExpiryPolicy> expiryPolicy; ConsumerFactories consumerFactories; + ProtocolRegistry protocolRegistry; mutable sys::Mutex linkClientPropertiesLock; framing::FieldTable linkClientProperties; @@ -233,6 +230,7 @@ class Broker : public sys::Runnable, public Plugin::Target, DataDir& getDataDir() { return dataDir; } Options& getOptions() { return config; } AsyncResultQueueImpl& getAsyncResultQueue() { return asyncResultQueue; } + ProtocolRegistry& getProtocolRegistry() { return protocolRegistry; } void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) { expiryPolicy = e; } boost::intrusive_ptr<ExpiryPolicy> getExpiryPolicy() { return expiryPolicy; } @@ -240,7 +238,7 @@ class Broker : public sys::Runnable, public Plugin::Target, SessionManager& getSessionManager() { return sessionManager; } const std::string& getFederationTag() const { return federationTag; } - QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject() const; + QPID_BROKER_EXTERN management::ManagementObject::shared_ptr GetManagementObject() const; QPID_BROKER_EXTERN management::Manageable* GetVhostObject() const; QPID_BROKER_EXTERN management::Manageable::status_t ManagementMethod( uint32_t methodId, management::Args& args, std::string& text); @@ -253,19 +251,17 @@ class Broker : public sys::Runnable, public Plugin::Target, QPID_BROKER_EXTERN void accept(); /** Create a connection to another broker. */ - void connect(const std::string& host, const std::string& port, + void connect(const std::string& name, + const std::string& host, const std::string& port, const std::string& transport, - boost::function2<void, int, std::string> failed, - sys::ConnectionCodec::Factory* =0); - /** Create a connection to another broker. */ - void connect(const Url& url, - boost::function2<void, int, std::string> failed, - sys::ConnectionCodec::Factory* =0); + boost::function2<void, int, std::string> failed); /** Move messages from one queue to another. A zero quantity means to move all messages + Return -1 if one of the queues does not exist, otherwise + the number of messages moved. */ - QPID_BROKER_EXTERN uint32_t queueMoveMessages( + QPID_BROKER_EXTERN int32_t queueMoveMessages( const std::string& srcQueue, const std::string& destQueue, uint32_t qty, @@ -277,46 +273,17 @@ class Broker : public sys::Runnable, public Plugin::Target, /** Expose poller so plugins can register their descriptors. */ QPID_BROKER_EXTERN boost::shared_ptr<sys::Poller> getPoller(); - boost::shared_ptr<sys::ConnectionCodec::Factory> getConnectionFactory() { return factory; } - void setConnectionFactory(boost::shared_ptr<sys::ConnectionCodec::Factory> f) { factory = f; } - /** Timer for local tasks affecting only this broker */ - sys::Timer& getTimer() { return timer; } - - /** Timer for tasks that must be synchronized if we are in a cluster */ - sys::Timer& getClusterTimer() { return clusterTimer.get() ? *clusterTimer : timer; } - QPID_BROKER_EXTERN void setClusterTimer(std::auto_ptr<sys::Timer>); + 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; } - - /** True of this broker is part of a cluster. - * Only valid after early initialization of plugins is complete. - */ - bool isInCluster() const { return inCluster; } - void setInCluster(bool set) { inCluster = set; } - - /** True if this broker is joining a cluster and in the process of - * receiving a state update. - */ - bool isClusterUpdatee() const { return clusterUpdatee; } - void setClusterUpdatee(bool set) { clusterUpdatee = set; } + bool inRecovery() const { return recoveryInProgress; } management::ManagementAgent* getManagementAgent() { return managementAgent.get(); } - /** - * Never true in a stand-alone broker. In a cluster, return true - * to defer delivery of messages deliveredg in a cluster-unsafe - * context. - *@return true if delivery of a message should be deferred. - */ - boost::function<bool (const std::string& queue, - const Message& msg)> deferDelivery; - bool isAuthenticating ( ) { return config.auth; } bool isTimestamping() { return config.timestampRcvMsgs; } @@ -371,8 +338,10 @@ class Broker : public sys::Runnable, public Plugin::Target, QPID_BROKER_EXTERN framing::FieldTable getLinkClientProperties() const; QPID_BROKER_EXTERN void setLinkClientProperties(const framing::FieldTable&); + QPID_BROKER_EXTERN uint16_t getLinkHearbeatInterval() { return config.linkHeartbeatInterval; } /** Information identifying this system */ boost::shared_ptr<const System> getSystem() const { return systemObject; } + friend class StatusCheckThread; }; }} diff --git a/cpp/src/qpid/broker/ConfigurationObserver.h b/cpp/src/qpid/broker/ConfigurationObserver.h index 701043db40..789490e08c 100644 --- a/cpp/src/qpid/broker/ConfigurationObserver.h +++ b/cpp/src/qpid/broker/ConfigurationObserver.h @@ -38,6 +38,10 @@ class Exchange; /** * Observer for changes to configuration (aka wiring) + * + * NOTE: create and destroy functions are called with + * the registry lock held. This is necessary to ensure + * they are called in the correct sequence. */ class ConfigurationObserver { diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp index e68c906cc2..df1a23f882 100644 --- a/cpp/src/qpid/broker/Connection.cpp +++ b/cpp/src/qpid/broker/Connection.cpp @@ -25,9 +25,9 @@ #include "qpid/broker/Bridge.h" #include "qpid/broker/Broker.h" #include "qpid/broker/Queue.h" -#include "qpid/broker/AclModule.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/sys/SecuritySettings.h" -#include "qpid/sys/ClusterSafe.h" +#include "qpid/sys/Timer.h" #include "qpid/log/Statement.h" #include "qpid/ptr_map.h" @@ -86,20 +86,14 @@ Connection::Connection(ConnectionOutputHandler* out_, std::string& mgmtId_, const qpid::sys::SecuritySettings& external, bool link_, - uint64_t objectId_, - bool shadow_, - bool delayManagement, - bool authenticated_ + uint64_t objectId_ ) : ConnectionState(out_, broker_), securitySettings(external), - shadow(shadow_), - authenticated(authenticated_), adapter(*this, link_), link(link_), mgmtClosing(false), mgmtId(mgmtId_), - mgmtObject(0), links(broker_.getLinks()), agent(0), timer(broker_.getTimer()), @@ -109,11 +103,6 @@ Connection::Connection(ConnectionOutputHandler* out_, { outboundTracker.wrap(out); broker.getConnectionObservers().connection(*this); - // In a cluster, allow adding the management object to be delayed. - if (!delayManagement) addManagementObject(); -} - -void Connection::addManagementObject() { assert(agent == 0); assert(mgmtObject == 0); Manageable* parent = broker.GetVhostObject(); @@ -121,8 +110,7 @@ void Connection::addManagementObject() { agent = broker.getManagementAgent(); if (agent != 0) { // TODO set last bool true if system connection - mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !link, false); - mgmtObject->set_shadow(shadow); + mgmtObject = _qmf::Connection::shared_ptr(new _qmf::Connection(agent, this, parent, mgmtId, !link, false, "AMQP 0-10")); agent->addObject(mgmtObject, objectId); } ConnectionState::setUrl(mgmtId); @@ -139,13 +127,11 @@ void Connection::requestIOProcessing(boost::function0<void> callback) Connection::~Connection() { if (mgmtObject != 0) { - mgmtObject->resourceDestroy(); - // In a cluster, Connections destroyed during shutdown are in - // a cluster-unsafe context. Don't raise an event in that case. - if (!link && isClusterSafe()) - agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId())); + if (!link) + agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId(), mgmtObject->get_remoteProperties())); QPID_LOG_CAT(debug, model, "Delete connection. user:" << ConnectionState::getUserId() << " rhost:" << mgmtId ); + mgmtObject->resourceDestroy(); } broker.getConnectionObservers().closed(*this); @@ -188,8 +174,7 @@ bool isMessage(const AMQMethodBody* method) void Connection::recordFromServer(const framing::AMQFrame& frame) { - // Don't record management stats in cluster-unsafe contexts - if (mgmtObject != 0 && isClusterSafe()) + if (mgmtObject != 0) { qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics(); cStats->framesToClient += 1; @@ -203,8 +188,7 @@ void Connection::recordFromServer(const framing::AMQFrame& frame) void Connection::recordFromClient(const framing::AMQFrame& frame) { - // Don't record management stats in cluster-unsafe contexts - if (mgmtObject != 0 && isClusterSafe()) + if (mgmtObject != 0) { qmf::org::apache::qpid::broker::Connection::PerThreadStats *cStats = mgmtObject->getStatistics(); cStats->framesFromClient += 1; @@ -279,28 +263,7 @@ void Connection::notifyConnectionForced(const string& text) void Connection::setUserId(const string& userId) { - // Account for changing userId - AclModule* acl = broker.getAcl(); - if (acl) - { - acl->setUserId(*this, userId); - } - ConnectionState::setUserId(userId); - // In a cluster, the cluster code will raise the connect event - // when the connection is replicated to the cluster. - if (!broker.isInCluster()) raiseConnectEvent(); -} - -void Connection::raiseConnectEvent() { - if (mgmtObject != 0) { - mgmtObject->set_authIdentity(userId); - agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId)); - } - - QPID_LOG_CAT(debug, model, "Create connection. user:" << userId - << " rhost:" << mgmtId ); - } void Connection::setUserProxyAuth(bool b) @@ -327,19 +290,6 @@ void Connection::close(connection::CloseCode code, const string& text) 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(); - if (linkHeartbeatTimer) { - linkHeartbeatTimer->cancel(); - } - adapter.close(connection::CLOSE_CODE_NORMAL, "OK"); - getOutput().close(); -} - void Connection::idleOut(){} void Connection::idleIn(){} @@ -364,9 +314,6 @@ void Connection::closed(){ // Physically closed, suspend open sessions. void Connection::doIoCallbacks() { if (!isOpen()) return; // Don't process IO callbacks until we are open. ScopedLock<Mutex> l(ioCallbackLock); - // Although IO callbacks execute in the connection thread context, they are - // not cluster safe because they are queued for execution in non-IO threads. - ClusterUnsafeScope cus; while (!ioCallbacks.empty()) { boost::function0<void> cb = ioCallbacks.front(); ioCallbacks.pop(); @@ -413,9 +360,9 @@ SessionHandler& Connection::getChannel(ChannelId id) { return *ptr_map_ptr(i); } -ManagementObject* Connection::GetManagementObject(void) const +ManagementObject::shared_ptr Connection::GetManagementObject(void) const { - return (ManagementObject*) mgmtObject; + return mgmtObject; } Manageable::status_t Connection::ManagementMethod(uint32_t methodId, Args&, string&) @@ -499,7 +446,7 @@ void Connection::abort() void Connection::setHeartbeatInterval(uint16_t heartbeat) { setHeartbeat(heartbeat); - if (heartbeat > 0 && !isShadow()) { + if (heartbeat > 0) { if (!heartbeatTimer) { heartbeatTimer = new ConnectionHeartbeatTask(heartbeat, timer, *this); timer.add(heartbeatTimer); @@ -535,7 +482,6 @@ void Connection::OutboundFrameTracker::close() { next->close(); } size_t Connection::OutboundFrameTracker::getBuffered() const { return next->getBuffered(); } void Connection::OutboundFrameTracker::abort() { next->abort(); } void Connection::OutboundFrameTracker::activateOutput() { next->activateOutput(); } -void Connection::OutboundFrameTracker::giveReadCredit(int32_t credit) { next->giveReadCredit(credit); } void Connection::OutboundFrameTracker::send(framing::AMQFrame& f) { next->send(f); diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h index d01599ce54..5c4d7132be 100644 --- a/cpp/src/qpid/broker/Connection.h +++ b/cpp/src/qpid/broker/Connection.h @@ -30,24 +30,13 @@ #include "qpid/broker/BrokerImportExport.h" #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_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/ConnectionInputHandler.h" -#include "qpid/sys/ConnectionOutputHandler.h" #include "qpid/sys/SecuritySettings.h" -#include "qpid/sys/Socket.h" -#include "qpid/sys/TimeoutHandler.h" #include "qpid/sys/Mutex.h" +#include "qpid/RefCounted.h" +#include "qpid/ptr_map.h" + +#include "qmf/org/apache/qpid/broker/Connection.h" #include <boost/ptr_container/ptr_map.hpp> #include <boost/bind.hpp> @@ -55,11 +44,17 @@ #include <algorithm> namespace qpid { +namespace sys { +class Timer; +class TimerTask; +} namespace broker { class Broker; class LinkRegistry; +class Queue; class SecureConnection; +class SessionHandler; struct ConnectionTimeoutTask; class Connection : public sys::ConnectionInputHandler, @@ -83,10 +78,7 @@ class Connection : public sys::ConnectionInputHandler, const std::string& mgmtId, const qpid::sys::SecuritySettings&, bool isLink = false, - uint64_t objectId = 0, - bool shadow=false, - bool delayManagement = false, - bool authenticated=true); + uint64_t objectId = 0); ~Connection (); @@ -112,7 +104,7 @@ class Connection : public sys::ConnectionInputHandler, void closeChannel(framing::ChannelId channel); // Manageable entry points - management::ManagementObject* GetManagementObject (void) const; + management::ManagementObject::shared_ptr GetManagementObject(void) const; management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string&); @@ -130,7 +122,6 @@ class Connection : public sys::ConnectionInputHandler, void notifyConnectionForced(const std::string& text); void setUserId(const std::string& uid); - void raiseConnectEvent(); // credentials for connected client const std::string& getUserId() const { return ConnectionState::getUserId(); } @@ -144,27 +135,14 @@ class Connection : public sys::ConnectionInputHandler, void setHeartbeatInterval(uint16_t heartbeat); void sendHeartbeat(); void restartTimeout(); - + template <class F> void eachSessionHandler(F f) { for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i) f(*ptr_map_ptr(i)); } - void sendClose(); void setSecureConnection(SecureConnection* secured); - /** True if this is a shadow connection in a cluster. */ - bool isShadow() const { return shadow; } - - /** True if this connection is authenticated */ - bool isAuthenticated() const { return authenticated; } - - // Used by cluster to update connection status - sys::AggregateOutput& getOutputTasks() { return outputTasks; } - - /** Cluster delays adding management object in the constructor then calls this. */ - void addManagementObject(); - const qpid::sys::SecuritySettings& getExternalSecuritySettings() const { return securitySettings; @@ -176,9 +154,6 @@ class Connection : public sys::ConnectionInputHandler, bool isLink() { return link; } void startLinkHeartbeatTimeoutTask(); - // Used by cluster during catch-up, see cluster::OutputInterceptor - void doIoCallbacks(); - void setClientProperties(const framing::FieldTable& cp) { clientProperties = cp; } const framing::FieldTable& getClientProperties() const { return clientProperties; } @@ -188,15 +163,13 @@ class Connection : public sys::ConnectionInputHandler, ChannelMap channels; qpid::sys::SecuritySettings securitySettings; - bool shadow; - bool authenticated; ConnectionHandler adapter; const bool link; bool mgmtClosing; const std::string mgmtId; sys::Mutex ioCallbackLock; std::queue<boost::function0<void> > ioCallbacks; - qmf::org::apache::qpid::broker::Connection* mgmtObject; + qmf::org::apache::qpid::broker::Connection::shared_ptr mgmtObject; LinkRegistry& links; management::ManagementAgent* agent; sys::Timer& timer; @@ -218,7 +191,6 @@ class Connection : public sys::ConnectionInputHandler, size_t getBuffered() const; void abort(); void activateOutput(); - void giveReadCredit(int32_t credit); void send(framing::AMQFrame&); void wrap(sys::ConnectionOutputHandlerPtr&); private: @@ -228,10 +200,11 @@ class Connection : public sys::ConnectionInputHandler, OutboundFrameTracker outboundTracker; void sent(const framing::AMQFrame& f); + void doIoCallbacks(); public: - qmf::org::apache::qpid::broker::Connection* getMgmtObject() { return mgmtObject; } + qmf::org::apache::qpid::broker::Connection::shared_ptr getMgmtObject() { return mgmtObject; } }; }} diff --git a/cpp/src/qpid/broker/ConnectionFactory.cpp b/cpp/src/qpid/broker/ConnectionFactory.cpp deleted file mode 100644 index d5d24ca629..0000000000 --- a/cpp/src/qpid/broker/ConnectionFactory.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -#include "qpid/broker/ConnectionFactory.h" -#include "qpid/framing/ProtocolVersion.h" -#include "qpid/amqp_0_10/Connection.h" -#include "qpid/broker/Connection.h" -#include "qpid/sys/SecuritySettings.h" -#include "qpid/log/Statement.h" - -namespace qpid { -namespace broker { - -using framing::ProtocolVersion; -using qpid::sys::SecuritySettings; -typedef std::auto_ptr<amqp_0_10::Connection> ConnectionPtr; -typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr; - -ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) {} - -ConnectionFactory::~ConnectionFactory() {} - -sys::ConnectionCodec* -ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, - const SecuritySettings& external) { - 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, external, false))); - return c.release(); - } - return 0; -} - -sys::ConnectionCodec* -ConnectionFactory::create(sys::OutputControl& out, const std::string& id, - const SecuritySettings& external) { - // used to create connections from one broker to another - ConnectionPtr c(new amqp_0_10::Connection(out, id, true)); - c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, true))); - return c.release(); -} - - -}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp index 06f442a47f..977c706ebd 100644 --- a/cpp/src/qpid/broker/ConnectionHandler.cpp +++ b/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -20,8 +20,10 @@ * */ -#include "qpid/SaslFactory.h" #include "qpid/broker/ConnectionHandler.h" + +#include "qpid/SaslFactory.h" +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" #include "qpid/broker/SecureConnection.h" #include "qpid/Url.h" @@ -30,8 +32,10 @@ #include "qpid/framing/enum.h" #include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/sys/SecurityLayer.h" #include "qpid/broker/AclModule.h" +#include "qpid/amqp_0_10/Codecs.h" #include "qmf/org/apache/qpid/broker/EventClientConnectFail.h" using namespace qpid; @@ -148,6 +152,24 @@ void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProp void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body) { + const framing::FieldTable& clientProperties = body.getClientProperties(); + qmf::org::apache::qpid::broker::Connection::shared_ptr mgmtObject = connection.getMgmtObject(); + + if (mgmtObject != 0) { + string procName = clientProperties.getAsString(CLIENT_PROCESS_NAME); + uint32_t pid = clientProperties.getAsInt(CLIENT_PID); + uint32_t ppid = clientProperties.getAsInt(CLIENT_PPID); + + types::Variant::Map properties; + qpid::amqp_0_10::translate(clientProperties, properties); + mgmtObject->set_remoteProperties(properties); + if (!procName.empty()) + mgmtObject->set_remoteProcessName(procName); + if (pid != 0) + mgmtObject->set_remotePid(pid); + if (ppid != 0) + mgmtObject->set_remoteParentPid(ppid); + } try { authenticator->start(body.getMechanism(), body.hasResponse() ? &body.getResponse() : 0); } catch (std::exception& /*e*/) { @@ -160,8 +182,9 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body) string uid; authenticator->getError(error); authenticator->getUid(uid); - if (agent) { - agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error)); + if (agent && mgmtObject) { + agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error, + mgmtObject->get_remoteProperties())); } QPID_LOG_CAT(debug, model, "Failed connection. rhost:" << connection.getMgmtId() << " user:" << uid @@ -169,9 +192,8 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body) } throw; } - const framing::FieldTable& clientProperties = body.getClientProperties(); - connection.setClientProperties(clientProperties); + connection.setClientProperties(clientProperties); connection.setFederationLink(clientProperties.get(QPID_FED_LINK)); if (clientProperties.isSet(QPID_FED_TAG)) { connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG)); @@ -187,19 +209,6 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body) } QPID_LOG(info, "Connection is a federation link"); } - - 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) @@ -216,8 +225,9 @@ void ConnectionHandler::Handler::secureOk(const string& response) string uid; authenticator->getError(error); authenticator->getUid(uid); - if (agent) { - agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error)); + if (agent && connection.getMgmtObject()) { + agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error, + connection.getMgmtObject()->get_remoteProperties())); } QPID_LOG_CAT(debug, model, "Failed connection. rhost:" << connection.getMgmtId() << " user:" << uid diff --git a/cpp/src/qpid/broker/ConnectionFactory.h b/cpp/src/qpid/broker/ConnectionState.cpp index 7c1a9a08e1..c6a8317c2b 100644 --- a/cpp/src/qpid/broker/ConnectionFactory.h +++ b/cpp/src/qpid/broker/ConnectionState.cpp @@ -1,5 +1,5 @@ /* - * + * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information @@ -7,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,34 +18,21 @@ * under the License. * */ -#ifndef _ConnectionFactory_ -#define _ConnectionFactory_ -#include "qpid/sys/ConnectionCodec.h" +#include "qpid/broker/ConnectionState.h" + +#include "qpid/broker/Broker.h" namespace qpid { namespace broker { -class Broker; - -class ConnectionFactory : public sys::ConnectionCodec::Factory -{ - public: - ConnectionFactory(Broker& b); - - virtual ~ConnectionFactory(); - - sys::ConnectionCodec* - create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id, - const qpid::sys::SecuritySettings&); - - sys::ConnectionCodec* - create(sys::OutputControl&, const std::string& id, const qpid::sys::SecuritySettings&); - - private: - Broker& broker; -}; +void ConnectionState::setUserId(const std::string& uid) { + userId = uid; + size_t at = userId.find('@'); + userName = userId.substr(0, at); + isDefaultRealm = ( + at!= std::string::npos && + getBroker().getOptions().realm == userId.substr(at+1,userId.size())); +} + }} - - -#endif diff --git a/cpp/src/qpid/broker/ConnectionState.h b/cpp/src/qpid/broker/ConnectionState.h index 4dfd86fd8e..a8d6e82210 100644 --- a/cpp/src/qpid/broker/ConnectionState.h +++ b/cpp/src/qpid/broker/ConnectionState.h @@ -21,18 +21,22 @@ #ifndef _ConnectionState_ #define _ConnectionState_ -#include <vector> - +#include "qpid/broker/ConnectionToken.h" #include "qpid/sys/AggregateOutput.h" #include "qpid/sys/ConnectionOutputHandlerPtr.h" #include "qpid/framing/ProtocolVersion.h" #include "qpid/management/Manageable.h" #include "qpid/Url.h" -#include "qpid/broker/Broker.h" + +#include <boost/function.hpp> +#include <vector> + namespace qpid { namespace broker { +class Broker; + class ConnectionState : public ConnectionToken, public management::Manageable { protected: @@ -46,9 +50,8 @@ class ConnectionState : public ConnectionToken, public management::Manageable framemax(65535), heartbeat(0), heartbeatmax(120), - userProxyAuth(false), // Can proxy msgs with non-matching auth ids when true (used by federation links & clustering) + userProxyAuth(false), // Can proxy msgs with non-matching auth ids when true (used by federation links) federationLink(true), - clusterOrderOut(0), isDefaultRealm(false) {} @@ -62,14 +65,7 @@ class ConnectionState : public ConnectionToken, public management::Manageable void setHeartbeat(uint16_t hb) { heartbeat = hb; } void setHeartbeatMax(uint16_t hbm) { heartbeatmax = hbm; } - virtual void setUserId(const std::string& uid) { - userId = uid; - size_t at = userId.find('@'); - userName = userId.substr(0, at); - isDefaultRealm = ( - at!= std::string::npos && - getBroker().getOptions().realm == userId.substr(at+1,userId.size())); - } + virtual void setUserId(const std::string& uid); const std::string& getUserId() const { return userId; } @@ -102,15 +98,6 @@ class ConnectionState : public ConnectionToken, public management::Manageable 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; } - virtual void requestIOProcessing (boost::function0<void>) = 0; protected: @@ -124,7 +111,6 @@ class ConnectionState : public ConnectionToken, public management::Manageable bool federationLink; std::string federationPeerTag; std::vector<Url> knownHosts; - framing::FrameHandler* clusterOrderOut; std::string userName; bool isDefaultRealm; }; diff --git a/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h index 64fc4288af..662b0f937d 100644 --- a/cpp/src/qpid/broker/Consumer.h +++ b/cpp/src/qpid/broker/Consumer.h @@ -79,6 +79,15 @@ class Consumer : public QueueCursor { */ virtual bool hideDeletedError() { return false; } + /** If false, the consumer is not counted for purposes of auto-deletion or + * immediate messages. This is used for "system" consumers that are created + * by the broker for internal purposes as opposed to consumers that are + * created by normal clients. + */ + virtual bool isCounted() { return true; } + + QueueCursor getCursor() const { return *this; } + void setCursor(const QueueCursor& qc) { static_cast<QueueCursor&>(*this) = qc; } protected: //framing::SequenceNumber position; diff --git a/cpp/src/qpid/broker/ConsumerFactory.h b/cpp/src/qpid/broker/ConsumerFactory.h index abd39fb3f8..1c0f2571e2 100644 --- a/cpp/src/qpid/broker/ConsumerFactory.h +++ b/cpp/src/qpid/broker/ConsumerFactory.h @@ -25,11 +25,14 @@ // TODO aconway 2011-11-25: it's ugly exposing SemanticState::ConsumerImpl in public. // Refactor to use a more abstract interface. -#include "qpid/broker/SemanticState.h" +#include <boost/shared_ptr.hpp> namespace qpid { namespace broker { +class SemanticState; +class SemanticStateConsumerImpl; + /** * Base class for consumer factoires. Plugins can register a * ConsumerFactory via Broker:: getConsumerFactories() Each time a @@ -41,7 +44,7 @@ class ConsumerFactory public: virtual ~ConsumerFactory() {} - virtual boost::shared_ptr<SemanticState::ConsumerImpl> create( + virtual boost::shared_ptr<SemanticStateConsumerImpl> create( SemanticState* parent, const std::string& name, boost::shared_ptr<Queue> queue, bool ack, bool acquire, bool exclusive, const std::string& tag, diff --git a/cpp/src/qpid/broker/DeliverableMessage.cpp b/cpp/src/qpid/broker/DeliverableMessage.cpp index be4b7f0796..31823709ce 100644 --- a/cpp/src/qpid/broker/DeliverableMessage.cpp +++ b/cpp/src/qpid/broker/DeliverableMessage.cpp @@ -36,8 +36,3 @@ Message& DeliverableMessage::getMessage() { return msg; } - -uint64_t DeliverableMessage::contentSize() -{ - return msg.getContentSize(); -} diff --git a/cpp/src/qpid/broker/DeliverableMessage.h b/cpp/src/qpid/broker/DeliverableMessage.h index d6d6bf5265..6e8275770d 100644 --- a/cpp/src/qpid/broker/DeliverableMessage.h +++ b/cpp/src/qpid/broker/DeliverableMessage.h @@ -36,7 +36,6 @@ namespace qpid { QPID_BROKER_EXTERN DeliverableMessage(const Message& msg, TxBuffer* txn); QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue); QPID_BROKER_EXTERN Message& getMessage(); - QPID_BROKER_EXTERN uint64_t contentSize(); virtual ~DeliverableMessage(){} }; } diff --git a/cpp/src/qpid/broker/DeliveryAdapter.h b/cpp/src/qpid/broker/DeliveryAdapter.h deleted file mode 100644 index e69de29bb2..0000000000 --- a/cpp/src/qpid/broker/DeliveryAdapter.h +++ /dev/null diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp index b1130c3ec0..6aef1c2168 100644 --- a/cpp/src/qpid/broker/DirectExchange.cpp +++ b/cpp/src/qpid/broker/DirectExchange.cpp @@ -70,7 +70,7 @@ bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, con if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { Mutex::ScopedLock l(lock); - Binding::shared_ptr b(new Binding(routingKey, queue, this, FieldTable(), fedOrigin)); + Binding::shared_ptr b(new Binding(routingKey, queue, this, args ? *args : FieldTable(), fedOrigin)); BoundKey& bk = bindings[routingKey]; if (exclusiveBinding) bk.queues.clear(); diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp index c55771c4e6..3b23ea2900 100644 --- a/cpp/src/qpid/broker/DtxManager.cpp +++ b/cpp/src/qpid/broker/DtxManager.cpp @@ -27,6 +27,9 @@ #include "qpid/ptr_map.h" #include <boost/format.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> + #include <iostream> using boost::intrusive_ptr; @@ -35,6 +38,30 @@ using qpid::ptr_map_ptr; using namespace qpid::broker; using namespace qpid::framing; +namespace { + typedef boost::function0<void> FireFunction; + struct DtxCleanup : public qpid::sys::TimerTask + { + FireFunction fireFunction; + + DtxCleanup(uint32_t timeout, FireFunction f); + void fire(); + }; + + DtxCleanup::DtxCleanup(uint32_t _timeout, FireFunction f) + : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxCleanup"), fireFunction(f){} + + void DtxCleanup::fire() + { + try { + fireFunction(); + } catch (qpid::ConnectionException& /*e*/) { + //assume it was explicitly cleaned up after a call to prepare, commit or rollback + } + } + +} + //DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(&t) {} DtxManager::DtxManager(qpid::sys::Timer& t) : asyncTxnStore(0), timer(&t) {} @@ -158,19 +185,7 @@ void DtxManager::timedout(const std::string& xid) } else { ptr_map_ptr(i)->timedout(); //TODO: do we want to have a timed task to cleanup, or can we rely on an explicit completion? - //timer.add(intrusive_ptr<TimerTask>(new DtxCleanup(60*30/*30 mins*/, *this, xid))); - } -} - -DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid) - : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxCleanup"), mgr(_mgr), xid(_xid) {} - -void DtxManager::DtxCleanup::fire() -{ - try { - mgr.remove(xid); - } catch (ConnectionException& /*e*/) { - //assume it was explicitly cleaned up after a call to prepare, commit or rollback + //timer->add(new DtxCleanup(60*30/*30 mins*/, boost::bind(&DtxManager::remove, this, xid))); } } diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h index cbc66d6391..82f7ebb9ac 100644 --- a/cpp/src/qpid/broker/DtxManager.h +++ b/cpp/src/qpid/broker/DtxManager.h @@ -32,20 +32,15 @@ #include "qpid/ptr_map.h" namespace qpid { +namespace sys { +class Timer; +} + namespace broker { class DtxManager{ typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap; - struct DtxCleanup : public sys::TimerTask - { - DtxManager& mgr; - const std::string& xid; - - DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid); - void fire(); - }; - WorkMap work; // TransactionalStore* store; AsyncTransactionalStore* asyncTxnStore; @@ -71,11 +66,6 @@ public: void setStore(AsyncTransactionalStore* const ats); void setTimer(sys::Timer& t) { timer = &t; } - // Used by cluster for replication. - template<class F> void each(F f) const { - for (WorkMap::const_iterator i = work.begin(); i != work.end(); ++i) - f(*ptr_map_ptr(i)); - } DtxWorkRecord* getWork(const std::string& xid); bool exists(const std::string& xid); static std::string convert(const framing::Xid& xid); diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp index 924f953eb8..13eaf4e568 100644 --- a/cpp/src/qpid/broker/DtxWorkRecord.cpp +++ b/cpp/src/qpid/broker/DtxWorkRecord.cpp @@ -20,7 +20,10 @@ */ #include "qpid/broker/DtxWorkRecord.h" #include "qpid/broker/DtxManager.h" +#include "qpid/broker/DtxTimeout.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/Timer.h" + #include <boost/format.hpp> #include <boost/mem_fn.hpp> using boost::mem_fn; @@ -39,6 +42,12 @@ DtxWorkRecord::~DtxWorkRecord() } } +void DtxWorkRecord::setTimeout(boost::intrusive_ptr<DtxTimeout> t) +{ timeout = t; } + +boost::intrusive_ptr<DtxTimeout> DtxWorkRecord::getTimeout() +{ return timeout; } + bool DtxWorkRecord::prepare() { Mutex::ScopedLock locker(lock); @@ -182,17 +191,3 @@ void DtxWorkRecord::timedout() } abort(); } - -size_t DtxWorkRecord::indexOf(const DtxBuffer::shared_ptr& buf) { - Work::iterator i = std::find(work.begin(), work.end(), buf); - if (i == work.end()) throw NotFoundException( - QPID_MSG("Can't find DTX buffer for xid: " << buf->getXid())); - return i - work.begin(); -} - -DtxBuffer::shared_ptr DtxWorkRecord::operator[](size_t i) const { - if (i > work.size()) - throw NotFoundException( - QPID_MSG("Can't find DTX buffer " << i << " for xid: " << xid)); - return work[i]; -} diff --git a/cpp/src/qpid/broker/DtxWorkRecord.h b/cpp/src/qpid/broker/DtxWorkRecord.h index 579579df2d..84344533d9 100644 --- a/cpp/src/qpid/broker/DtxWorkRecord.h +++ b/cpp/src/qpid/broker/DtxWorkRecord.h @@ -24,7 +24,6 @@ #include "qpid/broker/AsyncStore.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" @@ -39,6 +38,8 @@ namespace qpid { namespace broker { +struct DtxTimeout; + /** * Represents the work done under a particular distributed transaction * across potentially multiple channels. Identified by a xid. Allows @@ -74,19 +75,13 @@ public: 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; } - boost::intrusive_ptr<DtxTimeout> getTimeout() { return timeout; } + void setTimeout(boost::intrusive_ptr<DtxTimeout> t); + boost::intrusive_ptr<DtxTimeout> getTimeout(); std::string getXid() const { return xid; } bool isCompleted() const { return completed; } bool isRolledback() const { return rolledback; } bool isPrepared() const { return prepared; } bool isExpired() const { return expired; } - - // Used by cluster update; - size_t size() const { return work.size(); } - DtxBuffer::shared_ptr operator[](size_t i) const; - uint32_t getTimeout() const { return timeout? timeout->timeout : 0; } - size_t indexOf(const DtxBuffer::shared_ptr&); }; }} // qpid::broker diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp index 2414981481..855a69af98 100644 --- a/cpp/src/qpid/broker/Exchange.cpp +++ b/cpp/src/qpid/broker/Exchange.cpp @@ -167,19 +167,19 @@ void Exchange::routeIVE(){ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) : name(_name), durable(false), alternateUsers(0), persistenceId(0), sequence(false), - sequenceNo(0), ive(false), mgmtExchange(0), brokerMgmtObject(0), broker(b), destroyed(false) + sequenceNo(0), ive(false), broker(b), destroyed(false) { if (parent != 0 && broker != 0) { ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtExchange = new _qmf::Exchange (agent, this, parent, _name); + mgmtExchange = _qmf::Exchange::shared_ptr(new _qmf::Exchange (agent, this, parent, _name)); mgmtExchange->set_durable(durable); mgmtExchange->set_autoDelete(false); agent->addObject(mgmtExchange, 0, durable); if (broker) - brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject(); + brokerMgmtObject = boost::dynamic_pointer_cast<qmf::org::apache::qpid::broker::Broker>(broker->GetManagementObject()); } } } @@ -187,20 +187,20 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) : Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, Manageable* parent, Broker* b) : name(_name), durable(_durable), alternateUsers(0), persistenceId(0), - args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), brokerMgmtObject(0), broker(b), destroyed(false) + args(_args), sequence(false), sequenceNo(0), ive(false), broker(b), destroyed(false) { if (parent != 0 && broker != 0) { ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - mgmtExchange = new _qmf::Exchange (agent, this, parent, _name); + mgmtExchange = _qmf::Exchange::shared_ptr(new _qmf::Exchange (agent, this, parent, _name)); mgmtExchange->set_durable(durable); mgmtExchange->set_autoDelete(false); mgmtExchange->set_arguments(ManagementAgent::toMap(args)); agent->addObject(mgmtExchange, 0, durable); if (broker) - brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject(); + brokerMgmtObject = boost::dynamic_pointer_cast<qmf::org::apache::qpid::broker::Broker>(broker->GetManagementObject()); } } @@ -212,8 +212,6 @@ Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::Fiel ive = _args.get(qpidIVE); if (ive) { - if (broker && broker->isInCluster()) - throw framing::NotImplementedException("Cannot use Initial Value Exchanges in a cluster"); QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value"); } } @@ -227,6 +225,7 @@ Exchange::~Exchange () void Exchange::setAlternate(Exchange::shared_ptr _alternate) { alternate = _alternate; + alternate->incAlternateUsers(); if (mgmtExchange != 0) { if (alternate.get() != 0) mgmtExchange->set_altExchange(alternate->GetManagementObject()->getObjectId()); @@ -296,9 +295,9 @@ void Exchange::recoveryComplete(ExchangeRegistry& exchanges) } } -ManagementObject* Exchange::GetManagementObject (void) const +ManagementObject::shared_ptr Exchange::GetManagementObject (void) const { - return (ManagementObject*) mgmtExchange; + return mgmtExchange; } void Exchange::registerDynamicBridge(DynamicBridge* db, AsyncStore* const store) @@ -347,16 +346,16 @@ void Exchange::propagateFedOp(const string& routingKey, const string& tags, cons Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* _parent, FieldTable _args, const string& _origin, ConfigHandle _cfgHandle) - : parent(_parent), queue(_queue), key(_key), args(_args), origin(_origin), cfgHandle(_cfgHandle), mgmtBinding(0) + : parent(_parent), queue(_queue), key(_key), args(_args), origin(_origin), cfgHandle(_cfgHandle) { } Exchange::Binding::~Binding () { if (mgmtBinding != 0) { - ManagementObject* mo = queue->GetManagementObject(); + _qmf::Queue::shared_ptr mo = boost::dynamic_pointer_cast<_qmf::Queue>(queue->GetManagementObject()); if (mo != 0) - static_cast<_qmf::Queue*>(mo)->dec_bindingCount(); + mo->dec_bindingCount(); mgmtBinding->resourceDestroy (); } } @@ -369,25 +368,25 @@ void Exchange::Binding::startManagement() if (broker != 0) { ManagementAgent* agent = broker->getManagementAgent(); if (agent != 0) { - ManagementObject* mo = queue->GetManagementObject(); + _qmf::Queue::shared_ptr mo = boost::dynamic_pointer_cast<_qmf::Queue>(queue->GetManagementObject()); if (mo != 0) { management::ObjectId queueId = mo->getObjectId(); - mgmtBinding = new _qmf::Binding - (agent, this, (Manageable*) parent, queueId, key, ManagementAgent::toMap(args)); + mgmtBinding = _qmf::Binding::shared_ptr(new _qmf::Binding + (agent, this, (Manageable*) parent, queueId, key, ManagementAgent::toMap(args))); if (!origin.empty()) mgmtBinding->set_origin(origin); agent->addObject(mgmtBinding); - static_cast<_qmf::Queue*>(mo)->inc_bindingCount(); + mo->inc_bindingCount(); } } } } } -ManagementObject* Exchange::Binding::GetManagementObject () const +ManagementObject::shared_ptr Exchange::Binding::GetManagementObject () const { - return (ManagementObject*) mgmtBinding; + return mgmtBinding; } uint64_t Exchange::Binding::getSize() { return 0; } // TODO: kpvdr: implement persistence @@ -434,5 +433,10 @@ bool Exchange::routeWithAlternate(Deliverable& msg) return msg.delivered; } +void Exchange::setArgs(const framing::FieldTable& newArgs) { + args = newArgs; + if (mgmtExchange) mgmtExchange->set_arguments(ManagementAgent::toMap(args)); +} + }} diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h index df6d5d05a4..2c85e826ab 100644 --- a/cpp/src/qpid/broker/Exchange.h +++ b/cpp/src/qpid/broker/Exchange.h @@ -55,14 +55,14 @@ public: const framing::FieldTable args; std::string origin; ConfigHandle cfgHandle; - qmf::org::apache::qpid::broker::Binding* mgmtBinding; + qmf::org::apache::qpid::broker::Binding::shared_ptr mgmtBinding; Binding(const std::string& key, boost::shared_ptr<Queue> queue, Exchange* parent = 0, framing::FieldTable args = framing::FieldTable(), const std::string& origin = std::string(), ConfigHandle cfgHandle = ConfigHandle()); ~Binding(); void startManagement(); - management::ManagementObject* GetManagementObject() const; + management::ManagementObject::shared_ptr GetManagementObject() const; // DataSource implementation - allows for persistence uint64_t getSize(); @@ -170,8 +170,8 @@ protected: } }; - qmf::org::apache::qpid::broker::Exchange* mgmtExchange; - qmf::org::apache::qpid::broker::Broker* brokerMgmtObject; + qmf::org::apache::qpid::broker::Exchange::shared_ptr mgmtExchange; + qmf::org::apache::qpid::broker::Broker::shared_ptr brokerMgmtObject; public: typedef boost::shared_ptr<Exchange> shared_ptr; @@ -184,7 +184,8 @@ public: const std::string& getName() const { return name; } bool isDurable() { return durable; } - qpid::framing::FieldTable& getArgs() { return args; } + QPID_BROKER_EXTERN const qpid::framing::FieldTable& getArgs() const { return args; } + QPID_BROKER_EXTERN void setArgs(const framing::FieldTable&); QPID_BROKER_EXTERN Exchange::shared_ptr getAlternate() { return alternate; } QPID_BROKER_EXTERN void setAlternate(Exchange::shared_ptr _alternate); @@ -221,7 +222,7 @@ public: static QPID_BROKER_EXTERN Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer); // Manageable entry points - QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject(void) const; + QPID_BROKER_EXTERN management::ManagementObject::shared_ptr GetManagementObject(void) const; // Federation hooks class DynamicBridge { diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp index b31c7bd7b8..645918d526 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.cpp +++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -29,20 +29,26 @@ #include "qpid/management/ManagementDirectExchange.h" #include "qpid/management/ManagementTopicExchange.h" #include "qpid/framing/reply_exceptions.h" +#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h" +#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h" using namespace qpid::broker; using namespace qpid::sys; using std::pair; using std::string; using qpid::framing::FieldTable; +using qpid::management::ManagementAgent; +namespace _qmf = qmf::org::apache::qpid::broker; 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){ +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare( + const string& name, const string& type, bool durable, const FieldTable& args, + Exchange::shared_ptr alternate, const string& connectionId, const string& userId) +{ Exchange::shared_ptr exchange; std::pair<Exchange::shared_ptr, bool> result; { @@ -73,31 +79,55 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c } exchanges[name] = exchange; result = std::pair<Exchange::shared_ptr, bool>(exchange, true); + if (alternate) exchange->setAlternate(alternate); + // Call exchangeCreate inside the lock to ensure correct ordering. + if (broker) broker->getConfigurationObservers().exchangeCreate(exchange); } else { result = std::pair<Exchange::shared_ptr, bool>(i->second, false); } + if (broker && broker->getManagementAgent()) { + // Call raiseEvent inside the lock to ensure correct ordering. + broker->getManagementAgent()->raiseEvent( + _qmf::EventExchangeDeclare( + connectionId, + userId, + name, + type, + alternate ? alternate->getName() : string(), + durable, + false, + ManagementAgent::toMap(result.first->getArgs()), + result.second ? "created" : "existing")); + } } - if (broker && exchange) broker->getConfigurationObservers().exchangeCreate(exchange); return result; } -void ExchangeRegistry::destroy(const string& name){ +void ExchangeRegistry::destroy( + const string& name, const string& connectionId, const string& userId) +{ 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 << "'")); - Exchange::shared_ptr exchange; { RWlock::ScopedWlock locker(lock); ExchangeMap::iterator i = exchanges.find(name); if (i != exchanges.end()) { - exchange = i->second; + if (broker) { + // Call exchangeDestroy and raiseEvent inside the lock to ensure + // correct ordering. + broker->getConfigurationObservers().exchangeDestroy(i->second); + if (broker->getManagementAgent()) + broker->getManagementAgent()->raiseEvent( + _qmf::EventExchangeDelete(connectionId, userId, name)); + } i->second->destroy(); exchanges.erase(i); + } } - if (broker && exchange) broker->getConfigurationObservers().exchangeDestroy(exchange); } Exchange::shared_ptr ExchangeRegistry::find(const string& name){ diff --git a/cpp/src/qpid/broker/ExchangeRegistry.h b/cpp/src/qpid/broker/ExchangeRegistry.h index c5d2483a23..df4c24da39 100644 --- a/cpp/src/qpid/broker/ExchangeRegistry.h +++ b/cpp/src/qpid/broker/ExchangeRegistry.h @@ -46,14 +46,23 @@ class ExchangeRegistry{ 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 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(), + Exchange::shared_ptr alternate = Exchange::shared_ptr(), + const std::string& connectionId = std::string(), + const std::string& userId = std::string()); + + QPID_BROKER_EXTERN void destroy( + const std::string& name, + const std::string& connectionId = std::string(), + const std::string& userId = std::string()); + QPID_BROKER_EXTERN Exchange::shared_ptr getDefault(); /** diff --git a/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp index 941d909778..4ccb44ce53 100644 --- a/cpp/src/qpid/broker/FanOutExchange.cpp +++ b/cpp/src/qpid/broker/FanOutExchange.cpp @@ -54,7 +54,7 @@ bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const bool propagate = false; if (args == 0 || fedOp.empty() || fedOp == fedOpBind) { - Binding::shared_ptr binding (new Binding ("", queue, this, FieldTable(), fedOrigin)); + Binding::shared_ptr binding (new Binding ("", queue, this, args ? *args : FieldTable(), fedOrigin)); if (bindings.add_unless(binding, MatchQueue(queue))) { binding->startManagement(); propagate = fedBinding.addOrigin(queue->getName(), fedOrigin); diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp index 76ffa7a922..7edd54bc24 100644 --- a/cpp/src/qpid/broker/HeadersExchange.cpp +++ b/cpp/src/qpid/broker/HeadersExchange.cpp @@ -48,6 +48,7 @@ namespace { const std::string empty; // federation related args and values + const std::string QPID_RESERVED("qpid."); const std::string qpidFedOp("qpid.fed.op"); const std::string qpidFedTags("qpid.fed.tags"); const std::string qpidFedOrigin("qpid.fed.origin"); @@ -200,8 +201,8 @@ bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, co //matching (they are internally added properties //controlling binding propagation but not relevant to //actual routing) - Binding::shared_ptr binding (new Binding (bindingKey, queue, this, extra_args)); - BoundKey bk(binding); + Binding::shared_ptr binding (new Binding (bindingKey, queue, this, args ? *args : FieldTable())); + BoundKey bk(binding, extra_args); if (bindings.add_unless(bk, MatchArgs(queue, &extra_args))) { binding->startManagement(); propagate = bk.fedBinding.addOrigin(queue->getName(), fedOrigin); @@ -282,7 +283,7 @@ void HeadersExchange::route(Deliverable& msg) Bindings::ConstPtr p = bindings.snapshot(); if (p.get()) { for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) { - Matcher matcher(i->binding->args); + Matcher matcher(i->args); msg.getMessage().processProperties(matcher); if (matcher.matches()) { b->push_back(i->binding); @@ -298,7 +299,7 @@ bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, cons Bindings::ConstPtr p = bindings.snapshot(); if (p.get()){ for (std::vector<BoundKey>::const_iterator i = p->begin(); i != p->end(); ++i) { - if ( (!args || equal((*i).binding->args, *args)) && (!queue || (*i).binding->queue == queue)) { + if ( (!args || equal((*i).args, *args)) && (!queue || (*i).binding->queue == queue)) { return true; } } @@ -315,10 +316,7 @@ void HeadersExchange::getNonFedArgs(const FieldTable* args, FieldTable& nonFedAr for (qpid::framing::FieldTable::ValueMap::const_iterator i=args->begin(); i != args->end(); ++i) { - const string & name(i->first); - if (name == qpidFedOp || - name == qpidFedTags || - name == qpidFedOrigin) + if (i->first.find(QPID_RESERVED) == 0) { continue; } diff --git a/cpp/src/qpid/broker/HeadersExchange.h b/cpp/src/qpid/broker/HeadersExchange.h index 10bf9c8c0b..93e3929563 100644 --- a/cpp/src/qpid/broker/HeadersExchange.h +++ b/cpp/src/qpid/broker/HeadersExchange.h @@ -38,8 +38,9 @@ class HeadersExchange : public virtual Exchange { struct BoundKey { Binding::shared_ptr binding; + qpid::framing::FieldTable args; FedBinding fedBinding; - BoundKey(Binding::shared_ptr binding_) : binding(binding_) {} + BoundKey(Binding::shared_ptr binding_, const qpid::framing::FieldTable& args_) : binding(binding_), args(args_) {} }; struct MatchArgs diff --git a/cpp/src/qpid/broker/LegacyLVQ.cpp b/cpp/src/qpid/broker/LegacyLVQ.cpp deleted file mode 100644 index e69de29bb2..0000000000 --- a/cpp/src/qpid/broker/LegacyLVQ.cpp +++ /dev/null diff --git a/cpp/src/qpid/broker/LegacyLVQ.h b/cpp/src/qpid/broker/LegacyLVQ.h deleted file mode 100644 index e69de29bb2..0000000000 --- a/cpp/src/qpid/broker/LegacyLVQ.h +++ /dev/null diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp index 9727040c9b..bb5d5cb678 100644 --- a/cpp/src/qpid/broker/Link.cpp +++ b/cpp/src/qpid/broker/Link.cpp @@ -30,8 +30,10 @@ #include "qpid/log/Statement.h" #include "qpid/framing/enum.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/amqp_types.h" #include "qpid/broker/AclModule.h" #include "qpid/broker/Exchange.h" +#include "qpid/broker/NameGenerator.h" #include "qpid/UrlArray.h" namespace qpid { @@ -148,12 +150,12 @@ Link::Link(const string& _name, host(_host), port(_port), transport(_transport), durable(_durable), authMechanism(_authMechanism), username(_username), password(_password), - persistenceId(0), mgmtObject(0), broker(_broker), state(0), + persistenceId(0), broker(_broker), state(0), visitCount(0), currentInterval(1), - closing(false), reconnectNext(0), // Index of next address for reconnecting in url. - channelCounter(1), + nextFreeChannel(1), + freeChannels(1, framing::CHANNEL_MAX), connection(0), agent(0), listener(l), @@ -166,19 +168,15 @@ Link::Link(const string& _name, agent = broker->getManagementAgent(); if (agent != 0) { - mgmtObject = new _qmf::Link(agent, this, parent, name, durable); + mgmtObject = _qmf::Link::shared_ptr(new _qmf::Link(agent, this, parent, name, durable)); mgmtObject->set_host(host); mgmtObject->set_port(port); mgmtObject->set_transport(transport); agent->addObject(mgmtObject, 0, durable); } } - if (links->isPassive()) { - setStateLH(STATE_PASSIVE); - } else { - setStateLH(STATE_WAITING); - startConnectionLH(); - } + setStateLH(STATE_WAITING); + startConnectionLH(); broker->getTimer().add(timerTask); if (failover) { @@ -212,9 +210,6 @@ void Link::setStateLH (int newState) state = newState; - if (hideManagement()) - return; - switch (state) { case STATE_WAITING : mgmtObject->set_state("Waiting"); break; @@ -222,7 +217,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; + case STATE_CLOSING : mgmtObject->set_state("Closing"); break; } } @@ -233,40 +228,39 @@ 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, boost::lexical_cast<std::string>(port), transport, + broker->connect (name, host, boost::lexical_cast<std::string>(port), transport, boost::bind (&Link::closed, this, _1, _2)); - QPID_LOG (debug, "Inter-broker link connecting to " << host << ":" << port); + QPID_LOG (info, "Inter-broker link connecting to " << host << ":" << port); } catch(const std::exception& e) { QPID_LOG(error, "Link connection to " << host << ":" << port << " failed: " << e.what()); setStateLH(STATE_WAITING); - if (!hideManagement()) - mgmtObject->set_lastError (e.what()); + mgmtObject->set_lastError (e.what()); } } void Link::established(Connection* c) { - if (state == STATE_PASSIVE) return; stringstream addr; addr << host << ":" << port; QPID_LOG (info, "Inter-broker link established to " << addr.str()); - if (!hideManagement() && agent) + if (agent) agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str())); - bool isClosing = false; + bool isClosing = true; { Mutex::ScopedLock mutex(lock); - setStateLH(STATE_OPERATIONAL); - currentInterval = 1; - visitCount = 0; - connection = c; - isClosing = closing; + if (state != STATE_CLOSING) { + isClosing = false; + setStateLH(STATE_OPERATIONAL); + currentInterval = 1; + visitCount = 0; + connection = c; + c->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); + } } if (isClosing) destroy(); - else // Process any IO tasks bridges added before established. - c->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); } @@ -291,11 +285,12 @@ class DetachedCallback : public SessionHandler::ErrorListener { }; } -void Link::opened() { +void Link::opened() +{ Mutex::ScopedLock mutex(lock); - if (!connection) return; + if (!connection || state != STATE_OPERATIONAL) return; - if (!hideManagement() && connection->GetManagementObject()) { + if (connection->GetManagementObject()) { mgmtObject->set_connectionRef(connection->GetManagementObject()->getObjectId()); } @@ -350,37 +345,43 @@ void Link::opened() { } } + +// called when connection attempt fails (see startConnectionLH) void Link::closed(int, std::string text) { - Mutex::ScopedLock mutex(lock); QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text); - connection = 0; + bool isClosing = false; + { + Mutex::ScopedLock mutex(lock); + + connection = 0; - if (!hideManagement()) { mgmtObject->set_connectionRef(qpid::management::ObjectId()); if (state == STATE_OPERATIONAL && agent) { stringstream addr; addr << host << ":" << port; agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str())); } - } - for (Bridges::iterator i = active.begin(); i != active.end(); i++) { - (*i)->closed(); - created.push_back(*i); - } - active.clear(); + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + (*i)->closed(); + created.push_back(*i); + } + active.clear(); - if (state != STATE_FAILED && state != STATE_PASSIVE) - { - setStateLH(STATE_WAITING); - if (!hideManagement()) + if (state == STATE_CLOSING) { + isClosing = true; + } else if (state != STATE_FAILED) { + setStateLH(STATE_WAITING); mgmtObject->set_lastError (text); + } } + if (isClosing) destroy(); } -// Called in connection IO thread, cleans up the connection before destroying Link +// Cleans up the connection before destroying Link. Must be called in connection thread +// if the connection is active. Caller Note well: may call "delete this"! void Link::destroy () { Bridges toDelete; @@ -410,7 +411,9 @@ void Link::destroy () for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++) (*i)->close(); toDelete.clear(); - listener(this); // notify LinkRegistry that this Link has been destroyed + // notify LinkRegistry that this Link has been destroyed. Will result in "delete + // this" if LinkRegistry is holding the last shared pointer to *this + listener(this); } void Link::add(Bridge::shared_ptr bridge) @@ -452,7 +455,7 @@ void Link::ioThreadProcessing() { Mutex::ScopedLock mutex(lock); - if (state != STATE_OPERATIONAL || closing) + if (state != STATE_OPERATIONAL) return; // check for bridge session errors and recover @@ -489,9 +492,9 @@ void Link::ioThreadProcessing() void Link::maintenanceVisit () { Mutex::ScopedLock mutex(lock); - if (closing) return; - if (state == STATE_WAITING) - { + + switch (state) { + case STATE_WAITING: visitCount++; if (visitCount >= currentInterval) { @@ -504,11 +507,17 @@ void Link::maintenanceVisit () startConnectionLH(); } } + break; + + case STATE_OPERATIONAL: + if ((!active.empty() || !created.empty() || !cancellations.empty()) && + connection && connection->isOpen()) + connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); + break; + + default: // no-op for all other states + break; } - else if (state == STATE_OPERATIONAL && - (!active.empty() || !created.empty() || !cancellations.empty()) && - connection && connection->isOpen()) - connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this)); } void Link::reconnectLH(const Address& a) @@ -517,14 +526,13 @@ void Link::reconnectLH(const Address& a) port = a.port; transport = a.protocol; - if (!hideManagement()) { - stringstream errorString; - errorString << "Failing over to " << a; - mgmtObject->set_lastError(errorString.str()); - mgmtObject->set_host(host); - mgmtObject->set_port(port); - mgmtObject->set_transport(transport); - } + stringstream errorString; + errorString << "Failing over to " << a; + mgmtObject->set_lastError(errorString.str()); + mgmtObject->set_host(host); + mgmtObject->set_port(port); + mgmtObject->set_transport(transport); + startConnectionLH(); } @@ -541,26 +549,56 @@ bool Link::tryFailoverLH() { return false; } -// Management updates for a link are inconsistent in a cluster, so they are -// suppressed. -bool Link::hideManagement() const { - return !mgmtObject || ( broker && broker->isInCluster()); +// Allocate channel from link free pool +framing::ChannelId Link::nextChannel() +{ + Mutex::ScopedLock mutex(lock); + if (!freeChannels.empty()) { + // A free channel exists. + for (framing::ChannelId i = 1; i <= framing::CHANNEL_MAX; i++) + { + // extract proposed free channel + framing::ChannelId c = nextFreeChannel; + // calculate next free channel + if (framing::CHANNEL_MAX == nextFreeChannel) + nextFreeChannel = 1; + else + nextFreeChannel += 1; + // if proposed channel is free, use it + if (freeChannels.contains(c)) + { + freeChannels -= c; + QPID_LOG(debug, "Link " << name << " allocates channel: " << c); + return c; + } + } + assert (false); + } + + throw Exception(Msg() << "Link " << name << " channel pool is empty"); } -uint Link::nextChannel() +// Return channel to link free pool +void Link::returnChannel(framing::ChannelId c) { Mutex::ScopedLock mutex(lock); - if (channelCounter >= framing::CHANNEL_MAX) - channelCounter = 1; - return channelCounter++; + QPID_LOG(debug, "Link " << name << " frees channel: " << c); + freeChannels += c; } void Link::notifyConnectionForced(const string text) { - Mutex::ScopedLock mutex(lock); - setStateLH(STATE_FAILED); - if (!hideManagement()) - mgmtObject->set_lastError(text); + bool isClosing = false; + { + Mutex::ScopedLock mutex(lock); + if (state == STATE_CLOSING) { + isClosing = true; + } else { + setStateLH(STATE_FAILED); + mgmtObject->set_lastError(text); + } + } + if (isClosing) destroy(); } void Link::setPersistenceId(uint64_t id) const @@ -643,21 +681,32 @@ uint32_t Link::encodedSize() const + password.size() + 1; } -ManagementObject* Link::GetManagementObject (void) const +ManagementObject::shared_ptr Link::GetManagementObject(void) const { - return (ManagementObject*) mgmtObject; + return mgmtObject; } void Link::close() { QPID_LOG(debug, "Link::close(), link=" << name ); - Mutex::ScopedLock mutex(lock); - 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)); + bool destroy_now = false; + { + Mutex::ScopedLock mutex(lock); + if (state != STATE_CLOSING) { + int old_state = state; + setStateLH(STATE_CLOSING); + if (connection) { + //connection can only be closed on the connections own IO processing thread + connection->requestIOProcessing(boost::bind(&Link::destroy, this)); + } else if (old_state == STATE_CONNECTING) { + // cannot destroy Link now since a connection request is outstanding. + // destroy the link after we get a response (see Link::established, + // Link::closed, Link::notifyConnectionForced, etc). + } else { + destroy_now = true; + } } } + if (destroy_now) destroy(); } @@ -701,22 +750,6 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& te 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 " - << host << ":" << port); - } - } -} - - /** utility to clean up connection resources correctly */ void Link::closeConnection( const std::string& reason) { @@ -752,28 +785,6 @@ namespace { const std::string FAILOVER_INDEX("failover-index"); } -void Link::getState(framing::FieldTable& state) const -{ - state.clear(); - Mutex::ScopedLock mutex(lock); - if (!url.empty()) { - state.setString(FAILOVER_ADDRESSES, url.str()); - state.setInt(FAILOVER_INDEX, reconnectNext); - } -} - -void Link::setState(const framing::FieldTable& state) -{ - Mutex::ScopedLock mutex(lock); - if (state.isSet(FAILOVER_ADDRESSES)) { - Url failovers(state.getAsString(FAILOVER_ADDRESSES)); - setUrl(failovers); - } - if (state.isSet(FAILOVER_INDEX)) { - reconnectNext = state.getAsInt(FAILOVER_INDEX); - } -} - std::string Link::createName(const std::string& transport, const std::string& host, uint16_t port) @@ -784,14 +795,6 @@ std::string Link::createName(const std::string& transport, return linkName.str(); } - -bool Link::pendingConnection(const std::string& _host, uint16_t _port) const -{ - Mutex::ScopedLock mutex(lock); - return (isConnecting() && _port == port && _host == host); -} - - const std::string Link::exchangeTypeName("qpid.LinkExchange"); }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h index f0cb90e73b..01ddc68d97 100644 --- a/cpp/src/qpid/broker/Link.h +++ b/cpp/src/qpid/broker/Link.h @@ -69,12 +69,11 @@ class Link : public PersistableConfig, public management::Manageable { std::string username; std::string password; mutable uint64_t persistenceId; - qmf::org::apache::qpid::broker::Link* mgmtObject; + qmf::org::apache::qpid::broker::Link::shared_ptr mgmtObject; Broker* broker; int state; uint32_t visitCount; uint32_t currentInterval; - bool closing; Url url; // URL can contain many addresses. size_t reconnectNext; // Index for next re-connect attempt @@ -82,7 +81,8 @@ class Link : public PersistableConfig, public management::Manageable { Bridges created; // Bridges pending creation Bridges active; // Bridges active Bridges cancellations; // Bridges pending cancellation - uint channelCounter; + framing::ChannelId nextFreeChannel; + RangeSet<framing::ChannelId> freeChannels; Connection* connection; management::ManagementAgent* agent; boost::function<void(Link*)> listener; @@ -97,7 +97,7 @@ class Link : public PersistableConfig, public management::Manageable { 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 int STATE_CLOSING = 6; // Waiting for outstanding connect to complete first static const uint32_t MAX_INTERVAL = 32; @@ -106,7 +106,6 @@ class Link : public PersistableConfig, public management::Manageable { void destroy(); // Cleanup connection before link goes away void ioThreadProcessing(); // Called on connection's IO thread by request bool tryFailoverLH(); // Called during maintenance visit - bool hideManagement() const; void reconnectLH(const Address&); //called by LinkRegistry // connection management (called by LinkRegistry) @@ -115,7 +114,6 @@ class Link : public PersistableConfig, public management::Manageable { void closed(int, std::string); // Called when connection goes away void notifyConnectionForced(const std::string text); void closeConnection(const std::string& reason); - bool pendingConnection(const std::string& host, uint16_t port) const; // is Link trying to connect to this remote? friend class LinkRegistry; // to call established, opened, closed @@ -151,7 +149,8 @@ class Link : public PersistableConfig, public management::Manageable { bool isDurable() { return durable; } void maintenanceVisit (); - uint nextChannel(); + framing::ChannelId nextChannel(); // allocate channel from link free pool + void returnChannel(framing::ChannelId); // return channel to link free pool void add(Bridge::shared_ptr); void cancel(Bridge::shared_ptr); @@ -165,7 +164,6 @@ class Link : public PersistableConfig, public management::Manageable { std::string getPassword() { return password; } Broker* getBroker() { return broker; } - void setPassive(bool p); bool isConnecting() const { return state == STATE_CONNECTING; } // PersistableConfig: @@ -181,17 +179,13 @@ class Link : public PersistableConfig, public management::Manageable { static bool isEncodedLink(const std::string& key); // Manageable entry points - management::ManagementObject* GetManagementObject(void) const; + management::ManagementObject::shared_ptr GetManagementObject(void) const; management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&); // manage the exchange owned by this link static const std::string exchangeTypeName; static boost::shared_ptr<Exchange> linkExchangeFactory(const std::string& name); - // replicate internal state of this Link for clustering - void getState(framing::FieldTable& state) const; - void setState(const framing::FieldTable& state); - /** create a name for a link (if none supplied by user config) */ static std::string createName(const std::string& transport, const std::string& host, diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp index a79081b8ed..d048b9c05f 100644 --- a/cpp/src/qpid/broker/LinkRegistry.cpp +++ b/cpp/src/qpid/broker/LinkRegistry.cpp @@ -19,8 +19,10 @@ * */ #include "qpid/broker/LinkRegistry.h" -#include "qpid/broker/Link.h" + +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" +#include "qpid/broker/Link.h" #include "qpid/log/Statement.h" #include <iostream> #include <boost/format.hpp> @@ -42,8 +44,8 @@ namespace _qmf = qmf::org::apache::qpid::broker; // factored: The persistence element should be factored separately LinkRegistry::LinkRegistry () : broker(0), -// parent(0), store(0), passive(false), - parent(0), asyncStore(0), passive(false), +// parent(0), store(0), + parent(0), asyncStore(0), realm("") { } @@ -60,7 +62,8 @@ class LinkRegistryConnectionObserver : public ConnectionObserver { LinkRegistry::LinkRegistry (Broker* _broker) : broker(_broker), - parent(0), asyncStore(0), passive(false), +// parent(0), store(0), + parent(0), asyncStore(0), realm(broker->getOptions().realm) { broker->getConnectionObservers().add( @@ -118,10 +121,9 @@ pair<Link::shared_ptr, bool> LinkRegistry::declare(const string& name, boost::bind(&LinkRegistry::linkDestroyed, this, _1), durable, authMechanism, username, password, broker, parent, failover)); -// if (durable && store) store->create(*link); - if (durable && asyncStore) { -// store->create(*link); - // TODO: kpvdr: async create config (link) + if (durable && asyncStore && !broker->inRecovery()) { + //store->create(*link); + // TODO: kpvdr: async create config (link) } links[name] = link; pendingLinks[name] = link; @@ -218,9 +220,8 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(const std::string& name, args, init, queueName, altExchange)); bridges[name] = bridge; link.add(bridge); -// if (durable && store) - if (durable && asyncStore) { -// store->create(*bridge); + if (durable && asyncStore && !broker->inRecovery()) { + //store->create(*bridge); // TODO: kpvdr: Async create config (bridge) } @@ -264,6 +265,7 @@ void LinkRegistry::destroyBridge(Bridge *bridge) Link *link = b->second->getLink(); if (link) { link->cancel(b->second); + link->returnChannel( bridge->getChannel() ); } // if (b->second->isDurable()) if (b->second->isDurable()) { @@ -283,38 +285,6 @@ AsyncStore* LinkRegistry::getStore() const { return asyncStore; } -namespace { - void extractHostPort(const std::string& connId, std::string *host, uint16_t *port) - { - // Extract host and port of remote broker from connection id string. - // - // TODO aconway 2011-02-01: centralize code that constructs/parses connection - // management IDs. Currently sys:: protocol factories and IO plugins construct the - // IDs and LinkRegistry parses them. - // KAG: current connection id format assumed: - // "localhost:port-remotehost:port". In the case of IpV6, the host addresses are - // contained within brackets "[...]", example: - // connId="[::1]:36859-[::1]:48603". Liberal use of "asserts" provided to alert us - // if this assumption changes! - size_t separator = connId.find('-'); - assert(separator != std::string::npos); - std::string remote = connId.substr(separator+1, std::string::npos); - separator = remote.rfind(":"); - assert(separator != std::string::npos); - *host = remote.substr(0, separator); - // IPv6 - host is bracketed by "[]", strip them - if ((*host)[0] == '[' && (*host)[host->length() - 1] == ']') { - *host = host->substr(1, host->length() - 2); - } - try { - *port = boost::lexical_cast<uint16_t>(remote.substr(separator+1, std::string::npos)); - } catch (const boost::bad_lexical_cast&) { - QPID_LOG(error, "Invalid format for connection identifier! '" << connId << "'"); - assert(false); - } - } -} - /** find the Link that corresponds to the given connection */ Link::shared_ptr LinkRegistry::findLink(const std::string& connId) { @@ -334,19 +304,15 @@ void LinkRegistry::notifyConnection(const std::string& key, Connection* c) // create a mapping from connection id to link QPID_LOG(debug, "LinkRegistry::notifyConnection(); key=" << key ); std::string host; - uint16_t port = 0; - extractHostPort( key, &host, &port ); Link::shared_ptr link; { Mutex::ScopedLock locker(lock); - for (LinkMap::iterator l = pendingLinks.begin(); l != pendingLinks.end(); ++l) { - if (l->second->pendingConnection(host, port)) { - link = l->second; - pendingLinks.erase(l); - connections[key] = link->getName(); - QPID_LOG(debug, "LinkRegistry:: found pending =" << link->getName()); - break; - } + LinkMap::iterator l = pendingLinks.find(key); + if (l != pendingLinks.end()) { + link = l->second; + pendingLinks.erase(l); + connections[key] = link->getName(); + QPID_LOG(debug, "LinkRegistry:: found pending =" << link->getName()); } } @@ -461,26 +427,4 @@ std::string LinkRegistry::getAuthIdentity(const std::string& key) return link->getUsername(); } - -void LinkRegistry::setPassive(bool p) -{ - Mutex::ScopedLock locker(lock); - passive = p; - 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); - } -} - -void LinkRegistry::eachLink(boost::function<void(boost::shared_ptr<Link>)> f) { - Mutex::ScopedLock locker(lock); - for (LinkMap::iterator i = links.begin(); i != links.end(); ++i) f(i->second); -} - -void LinkRegistry::eachBridge(boost::function<void(boost::shared_ptr<Bridge>)> f) { - Mutex::ScopedLock locker(lock); - for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) f(i->second); -} - }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h index 17f45a60c8..6689ceda0f 100644 --- a/cpp/src/qpid/broker/LinkRegistry.h +++ b/cpp/src/qpid/broker/LinkRegistry.h @@ -54,7 +54,6 @@ namespace broker { management::Manageable* parent; // MessageStore* store; AsyncStore* asyncStore; - bool passive; std::string realm; boost::shared_ptr<Link> findLink(const std::string& key); @@ -147,20 +146,6 @@ namespace broker { QPID_BROKER_EXTERN std::string getPassword (const std::string& key); QPID_BROKER_EXTERN std::string getHost (const std::string& key); QPID_BROKER_EXTERN uint16_t getPort (const std::string& key); - - /** - * Called 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. - */ - QPID_BROKER_EXTERN void setPassive(bool); - QPID_BROKER_EXTERN bool isPassive() { return passive; } - - /** Iterate over each link in the registry. Used for cluster updates. */ - QPID_BROKER_EXTERN void eachLink(boost::function<void(boost::shared_ptr<Link>)> f); - /** Iterate over each bridge in the registry. Used for cluster updates. */ - QPID_BROKER_EXTERN void eachBridge(boost::function<void(boost::shared_ptr< Bridge>)> f); }; } } diff --git a/cpp/src/qpid/broker/Lvq.cpp b/cpp/src/qpid/broker/Lvq.cpp index f5e66c8a74..9d6923e560 100644 --- a/cpp/src/qpid/broker/Lvq.cpp +++ b/cpp/src/qpid/broker/Lvq.cpp @@ -20,7 +20,6 @@ */ #include "Lvq.h" #include "MessageMap.h" -#include "qpid/sys/ClusterSafe.h" #include "qpid/sys/Monitor.h" namespace qpid { @@ -35,7 +34,6 @@ Lvq::Lvq(const std::string& n, std::auto_ptr<MessageMap> m, const QueueSettings& void Lvq::push(Message& message, bool isRecovery) { - qpid::sys::assertClusterSafe(); QueueListeners::NotificationSet copy; Message old; bool removed; diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp index c48e9bcfa4..431f4291ff 100644 --- a/cpp/src/qpid/broker/Message.cpp +++ b/cpp/src/qpid/broker/Message.cpp @@ -131,6 +131,18 @@ uint64_t Message::getTtl() const } } +bool Message::getTtl(uint64_t ttl) const +{ + if (encoding->getTtl(ttl) && expiration < FAR_FUTURE) { + sys::Duration remaining(sys::AbsTime::now(), getExpiration()); + // convert from ns to ms; set to 0 if expired + ttl = (int64_t(remaining) >= 1000000 ? int64_t(remaining)/1000000 : 0); + return true; + } else { + return false; + } +} + void Message::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e) { //TODO: this is still quite 0-10 specific... diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h index 599819d7b6..b999a7d5a3 100644 --- a/cpp/src/qpid/broker/Message.h +++ b/cpp/src/qpid/broker/Message.h @@ -61,7 +61,6 @@ public: virtual std::string getPropertyAsString(const std::string& key) const = 0; virtual std::string getAnnotationAsString(const std::string& key) const = 0; virtual bool getTtl(uint64_t&) const = 0; - virtual bool hasExpiration() const = 0; virtual std::string getContent() const = 0; virtual void processProperties(MapHandler&) const = 0; virtual std::string getUserId() const = 0; @@ -92,6 +91,7 @@ public: sys::AbsTime getExpiration() const { return expiration; } void setExpiration(sys::AbsTime exp) { expiration = exp; } uint64_t getTtl() const; + bool getTtl(uint64_t) const; /** set the timestamp delivery property to the current time-of-day */ QPID_BROKER_EXTERN void setTimestamp(); @@ -125,7 +125,7 @@ public: QPID_BROKER_EXTERN const qpid::types::Variant::Map& getAnnotations() const; std::string getUserId() const; - QPID_BROKER_EXTERN std::string getContent() const;//TODO: may be better to get rid of this... + QPID_BROKER_EXTERN std::string getContent() const;//Used for ha, management, when content needs to be decoded QPID_BROKER_EXTERN boost::intrusive_ptr<AsyncCompletion> getIngressCompletion() const; QPID_BROKER_EXTERN boost::intrusive_ptr<PersistableMessage> getPersistentContext() const; diff --git a/cpp/src/qpid/broker/MessageGroupManager.cpp b/cpp/src/qpid/broker/MessageGroupManager.cpp index 47e40a4794..c083e4ee0f 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.cpp +++ b/cpp/src/qpid/broker/MessageGroupManager.cpp @@ -302,19 +302,6 @@ void MessageGroupManager::setDefaults(const std::string& groupId) // static defaultGroupId = groupId; } -/** Cluster replication: - - state map format: - - { "group-state": [ {"name": <group-name>, - "owner": <consumer-name>-or-empty, - "acquired-ct": <acquired count>, - "positions": [Seqnumbers, ... ]}, - {...} - ] - } -*/ - namespace { const std::string GROUP_NAME("name"); const std::string GROUP_OWNER("owner"); @@ -324,100 +311,3 @@ namespace { const std::string GROUP_STATE("group-state"); } - -/** Runs on UPDATER to snapshot current state */ -void MessageGroupManager::getState(qpid::framing::FieldTable& state ) const -{ - using namespace qpid::framing; - state.clear(); - framing::Array groupState(TYPE_CODE_MAP); - for (GroupMap::const_iterator g = messageGroups.begin(); - g != messageGroups.end(); ++g) { - - framing::FieldTable group; - group.setString(GROUP_NAME, g->first); - group.setString(GROUP_OWNER, g->second.owner); - group.setInt(GROUP_ACQUIRED_CT, g->second.acquired); - framing::Array positions(TYPE_CODE_UINT32); - framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN); - for (GroupState::MessageFifo::const_iterator p = g->second.members.begin(); - p != g->second.members.end(); ++p) { - positions.push_back(framing::Array::ValuePtr(new IntegerValue( p->position ))); - acquiredMsgs.push_back(framing::Array::ValuePtr(new BoolValue( p->acquired ))); - } - group.setArray(GROUP_POSITIONS, positions); - group.setArray(GROUP_ACQUIRED_MSGS, acquiredMsgs); - groupState.push_back(framing::Array::ValuePtr(new FieldTableValue(group))); - } - state.setArray(GROUP_STATE, groupState); - - QPID_LOG(debug, "Queue \"" << qName << "\": replicating message group state, key=" << groupIdHeader); -} - - -/** called on UPDATEE to set state from snapshot */ -void MessageGroupManager::setState(const qpid::framing::FieldTable& state) -{ - using namespace qpid::framing; - messageGroups.clear(); - freeGroups.clear(); - cachedGroup = 0; - - framing::Array groupState(TYPE_CODE_MAP); - - bool ok = state.getArray(GROUP_STATE, groupState); - if (!ok) { - QPID_LOG(error, "Unable to find message group state information for queue \"" << - qName << "\": cluster inconsistency error!"); - return; - } - - for (framing::Array::const_iterator g = groupState.begin(); - g != groupState.end(); ++g) { - framing::FieldTable group; - ok = framing::getEncodedValue<FieldTable>(*g, group); - if (!ok) { - QPID_LOG(error, "Invalid message group state information for queue \"" << - qName << "\": table encoding error!"); - return; - } - MessageGroupManager::GroupState state; - if (!group.isSet(GROUP_NAME) || !group.isSet(GROUP_OWNER) || !group.isSet(GROUP_ACQUIRED_CT)) { - QPID_LOG(error, "Invalid message group state information for queue \"" << - qName << "\": fields missing error!"); - return; - } - state.group = group.getAsString(GROUP_NAME); - state.owner = group.getAsString(GROUP_OWNER); - state.acquired = group.getAsInt(GROUP_ACQUIRED_CT); - framing::Array positions(TYPE_CODE_UINT32); - ok = group.getArray(GROUP_POSITIONS, positions); - if (!ok) { - QPID_LOG(error, "Invalid message group state information for queue \"" << - qName << "\": position encoding error!"); - return; - } - framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN); - ok = group.getArray(GROUP_ACQUIRED_MSGS, acquiredMsgs); - if (!ok || positions.count() != acquiredMsgs.count()) { - QPID_LOG(error, "Invalid message group state information for queue \"" << - qName << "\": acquired flag encoding error!"); - return; - } - - Array::const_iterator a = acquiredMsgs.begin(); - for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) { - GroupState::MessageState mState((*p)->getIntegerValue<uint32_t, 4>()); - mState.acquired = (*a++)->getIntegerValue<bool>(); - state.members.push_back(mState); - } - - messageGroups[state.group] = state; - if (!state.owned()) { - assert(state.members.size()); - freeGroups[state.members.front().position] = &messageGroups[state.group]; - } - } - - QPID_LOG(debug, "Queue \"" << qName << "\": message group state replicated, key =" << groupIdHeader) -} diff --git a/cpp/src/qpid/broker/MessageGroupManager.h b/cpp/src/qpid/broker/MessageGroupManager.h index fe39e007b5..bf45e776c8 100644 --- a/cpp/src/qpid/broker/MessageGroupManager.h +++ b/cpp/src/qpid/broker/MessageGroupManager.h @@ -25,11 +25,12 @@ /* for managing message grouping on Queues */ #include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/StatefulQueueObserver.h" +#include "qpid/broker/QueueObserver.h" #include "qpid/broker/MessageDistributor.h" #include "qpid/framing/SequenceNumber.h" #include "qpid/sys/unordered_map.h" +#include "boost/shared_ptr.hpp" #include <deque> namespace qpid { @@ -39,8 +40,9 @@ class QueueObserver; struct QueueSettings; class MessageDistributor; class Messages; +class Consumer; -class MessageGroupManager : public StatefulQueueObserver, public MessageDistributor +class MessageGroupManager : public QueueObserver, public MessageDistributor { static std::string defaultGroupId; // assigned if no group id header present @@ -101,10 +103,10 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu MessageGroupManager(const std::string& header, const std::string& _qName, Messages& container, unsigned int _timestamp=0 ) - : StatefulQueueObserver(std::string("MessageGroupManager:") + header), - groupIdHeader( header ), timestamp(_timestamp), messages(container), qName(_qName), - hits(0), misses(0), - lastMsg(0), cachedGroup(0) {} + : groupIdHeader( header ), timestamp(_timestamp), messages(container), + qName(_qName), + hits(0), misses(0), + lastMsg(0), cachedGroup(0) {} virtual ~MessageGroupManager(); // QueueObserver iface @@ -114,8 +116,6 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu void dequeued( const Message& qm ); void consumerAdded( const Consumer& ) {}; void consumerRemoved( const Consumer& ) {}; - void getState(qpid::framing::FieldTable& state ) const; - void setState(const qpid::framing::FieldTable&); // MessageDistributor iface bool acquire(const std::string& c, Message& ); diff --git a/cpp/src/qpid/broker/MessageStore.h b/cpp/src/qpid/broker/MessageStore.h index 5e339574fd..d6a7cea18b 100644 --- a/cpp/src/qpid/broker/MessageStore.h +++ b/cpp/src/qpid/broker/MessageStore.h @@ -49,20 +49,6 @@ class MessageStore : public TransactionalStore, public Recoverable { public: /** - * If called after initialization but before recovery, will discard the database - * content and reinitialize as though it were a new installation. If the parameter - * saveStoreContent is true, the content of the store will be saved in such a way - * that the truncate can be reversed. This is used when cluster nodes recover and - * must get their content from a cluster sync rather than directly from the store. - * - * @param saveStoreContent If true, will move content of the store to a backup - * location where they may be restored later if needed. It is - * not necessary to save more than one prior version of the - * store. - */ - virtual void truncateInit(const bool saveStoreContent = false) = 0; - - /** * Record the existence of a durable queue */ virtual void create(PersistableQueue& queue, diff --git a/cpp/src/qpid/broker/MessageStoreModule.cpp b/cpp/src/qpid/broker/MessageStoreModule.cpp index 4309ee8524..9214f5a732 100644 --- a/cpp/src/qpid/broker/MessageStoreModule.cpp +++ b/cpp/src/qpid/broker/MessageStoreModule.cpp @@ -44,11 +44,6 @@ MessageStoreModule::~MessageStoreModule() 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)); diff --git a/cpp/src/qpid/broker/MessageStoreModule.h b/cpp/src/qpid/broker/MessageStoreModule.h index e5c271f4fa..89d6ebdecb 100644 --- a/cpp/src/qpid/broker/MessageStoreModule.h +++ b/cpp/src/qpid/broker/MessageStoreModule.h @@ -44,7 +44,6 @@ class MessageStoreModule : public MessageStore 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); diff --git a/cpp/src/qpid/broker/Messages.h b/cpp/src/qpid/broker/Messages.h index a94ac7e0bf..cd846a4973 100644 --- a/cpp/src/qpid/broker/Messages.h +++ b/cpp/src/qpid/broker/Messages.h @@ -91,13 +91,6 @@ class Messages virtual Message* find(const QueueCursor&) = 0; /** - * Add an already acquired message to the queue. - * Used by a cluster updatee to replicate acquired messages from the updater. - * Only need be implemented by subclasses that keep track of - * acquired messages. - */ - //virtual void updateAcquired(const QueuedMessage&) { } - /** * Apply, the functor to each message held */ virtual void foreach(Functor) = 0; diff --git a/cpp/src/qpid/broker/NullMessageStore.cpp b/cpp/src/qpid/broker/NullMessageStore.cpp index 209941875a..a038c4db63 100644 --- a/cpp/src/qpid/broker/NullMessageStore.cpp +++ b/cpp/src/qpid/broker/NullMessageStore.cpp @@ -54,8 +54,6 @@ NullMessageStore::NullMessageStore() : 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*/) { queue.setPersistenceId(nextPersistenceId++); diff --git a/cpp/src/qpid/broker/NullMessageStore.h b/cpp/src/qpid/broker/NullMessageStore.h index 799bf6b368..4b108e0331 100644 --- a/cpp/src/qpid/broker/NullMessageStore.h +++ b/cpp/src/qpid/broker/NullMessageStore.h @@ -47,7 +47,6 @@ class QPID_BROKER_CLASS_EXTERN NullMessageStore : public MessageStore QPID_BROKER_EXTERN NullMessageStore(); 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); diff --git a/cpp/src/qpid/broker/Protocol.cpp b/cpp/src/qpid/broker/Protocol.cpp new file mode 100644 index 0000000000..e236698142 --- /dev/null +++ b/cpp/src/qpid/broker/Protocol.cpp @@ -0,0 +1,70 @@ +/* + * + * 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 "Protocol.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { + +qpid::sys::ConnectionCodec* ProtocolRegistry::create(const qpid::framing::ProtocolVersion& v, qpid::sys::OutputControl& o, const std::string& id, const qpid::sys::SecuritySettings& s) +{ + qpid::sys::ConnectionCodec* codec = 0; + for (Protocols::const_iterator i = protocols.begin(); !codec && i != protocols.end(); ++i) { + codec = i->second->create(v, o, id, s); + } + return codec; +} +boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> ProtocolRegistry::translate(const Message& m) +{ + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> transfer; + const qpid::broker::amqp_0_10::MessageTransfer* ptr = dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&m.getEncoding()); + if (ptr) transfer = boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer>(ptr); + for (Protocols::const_iterator i = protocols.begin(); !transfer && i != protocols.end(); ++i) { + transfer = i->second->translate(m); + } + if (!transfer) throw new Exception("Could not convert message into 0-10"); + return transfer; +} +boost::shared_ptr<RecoverableMessage> ProtocolRegistry::recover(qpid::framing::Buffer& b) +{ + boost::shared_ptr<RecoverableMessage> msg; + for (Protocols::const_iterator i = protocols.begin(); !msg && i != protocols.end(); ++i) { + msg = i->second->recover(b); + } + return msg; +} + +ProtocolRegistry::~ProtocolRegistry() +{ + for (Protocols::const_iterator i = protocols.begin(); i != protocols.end(); ++i) { + delete i->second; + } + protocols.clear(); +} +void ProtocolRegistry::add(const std::string& key, Protocol* protocol) +{ + protocols[key] = protocol; + QPID_LOG(info, "Loaded protocol " << key); +} + +}} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/Protocol.h b/cpp/src/qpid/broker/Protocol.h new file mode 100644 index 0000000000..2f268748fb --- /dev/null +++ b/cpp/src/qpid/broker/Protocol.h @@ -0,0 +1,82 @@ +#ifndef QPID_BROKER_PROTOCOL_H +#define QPID_BROKER_PROTOCOL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <map> +#include <string> +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace sys { +class ConnectionCodec; +class OutputControl; +struct SecuritySettings; +} +namespace framing { +class Buffer; +class ProtocolVersion; +} +namespace broker { +class Message; +class RecoverableMessage; +namespace amqp_0_10 { +class MessageTransfer; +} + +/** + * A simple abstraction allowing pluggable protocol(s) + * (versions). AMQP 0-10 is considered the default. Alternatives must + * provide a ConnectionCodec for encoding/decoding the protocol in + * full, a means of translating the native message format of that + * protocol into AMQP 0-10 and a means of recovering durable messages + * from disk. + */ +class Protocol +{ + public: + virtual ~Protocol() {} + virtual qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&) = 0; + virtual boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const Message&) = 0; + virtual boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&) = 0; + + private: +}; + +class ProtocolRegistry : public Protocol +{ + public: + qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const Message&); + boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&); + + ~ProtocolRegistry(); + void add(const std::string&, Protocol*); + private: + //name may be useful for descriptive purposes or even for some + //limited manipulation of ordering + typedef std::map<std::string, Protocol*> Protocols; + Protocols protocols; +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_PROTOCOL_H*/ diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp index f595b81724..bab6f2ea55 100644 --- a/cpp/src/qpid/broker/Queue.cpp +++ b/cpp/src/qpid/broker/Queue.cpp @@ -44,9 +44,9 @@ #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/FieldValue.h" -#include "qpid/sys/ClusterSafe.h" #include "qpid/sys/Monitor.h" #include "qpid/sys/Time.h" +#include "qpid/sys/Timer.h" #include "qpid/types/Variant.h" #include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h" #include "qmf/org/apache/qpid/broker/ArgsQueueReroute.h" @@ -79,8 +79,8 @@ namespace { inline void mgntEnqStats(const Message& msg, - _qmf::Queue* mgmtObject, - _qmf::Broker* brokerMgmtObject) + _qmf::Queue::shared_ptr mgmtObject, + _qmf::Broker::shared_ptr brokerMgmtObject) { if (mgmtObject != 0) { _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); @@ -103,8 +103,8 @@ inline void mgntEnqStats(const Message& msg, } inline void mgntDeqStats(const Message& msg, - _qmf::Queue* mgmtObject, - _qmf::Broker* brokerMgmtObject) + _qmf::Queue::shared_ptr mgmtObject, + _qmf::Broker::shared_ptr brokerMgmtObject) { if (mgmtObject != 0){ _qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics(); @@ -166,13 +166,11 @@ void Queue::TxPublish::rollback() throw() } Queue::Queue(const string& _name, const QueueSettings& _settings, -// MessageStore* const _store, AsyncStore* const _asyncStore, Manageable* parent, Broker* b) : name(_name), -// store(_store), asyncStore(_asyncStore), owner(0), consumerCount(0), @@ -183,8 +181,6 @@ Queue::Queue(const string& _name, const QueueSettings& _settings, messages(new MessageDeque()), persistenceId(0), settings(b ? merge(_settings, b->getOptions()) : _settings), - mgmtObject(0), - brokerMgmtObject(0), eventMode(0), broker(b), deleted(false), @@ -199,27 +195,24 @@ Queue::Queue(const string& _name, const QueueSettings& _settings, qpid::amqp_0_10::translate(settings.asMap(), encodableSettings); if (parent != 0 && broker != 0) { ManagementAgent* agent = broker->getManagementAgent(); - if (agent != 0) { -// mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, settings.autodelete); - mgmtObject = new _qmf::Queue(agent, this, parent, _name, _asyncStore != 0, settings.autodelete); + mgmtObject = _qmf::Queue::shared_ptr( + new _qmf::Queue(agent, this, parent, _name, _asyncStore != 0, settings.autodelete)); mgmtObject->set_arguments(settings.asMap()); -// agent->addObject(mgmtObject, 0, store != 0); agent->addObject(mgmtObject, 0, asyncStore != 0); - brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject(); + brokerMgmtObject = boost::dynamic_pointer_cast<_qmf::Broker>(broker->GetManagementObject()); if (brokerMgmtObject) brokerMgmtObject->inc_queueCount(); } } + + if ( settings.isBrowseOnly ) { + QPID_LOG ( info, "Queue " << name << " is browse-only." ); + } } Queue::~Queue() { - if (mgmtObject != 0) { - mgmtObject->resourceDestroy(); - if (brokerMgmtObject) - brokerMgmtObject->dec_queueCount(); - } } bool isLocalTo(const OwnershipToken* token, const Message& msg) @@ -246,9 +239,6 @@ void Queue::deliver(Message msg, TxBuffer* txn){ //'link' for whatever protocol is used; that would let protocol //specific stuff be kept out the queue - // Check for deferred delivery in a cluster. - if (broker && broker->deferDelivery(name, msg)) - return; if (broker::amqp_0_10::MessageTransfer::isImmediateDeliveryRequired(msg) && getConsumerCount() == 0) { if (alternateExchange) { DeliverableMessage deliverable(msg, 0); @@ -307,7 +297,6 @@ void Queue::process(Message& msg) void Queue::release(const QueueCursor& position, bool markRedelivered) { - assertClusterSafe(); QueueListeners::NotificationSet copy; { Mutex::ScopedLock locker(messageLock); @@ -333,7 +322,6 @@ bool Queue::dequeueMessageAt(const SequenceNumber& position) boost::intrusive_ptr<PersistableMessage> pmsg; { Mutex::ScopedLock locker(messageLock); - assertClusterSafe(); QPID_LOG(debug, "Attempting to dequeue message at " << position); QueueCursor cursor; Message* msg = messages->find(position, &cursor); @@ -353,7 +341,6 @@ bool Queue::dequeueMessageAt(const SequenceNumber& position) bool Queue::acquire(const QueueCursor& position, const std::string& consumer) { Mutex::ScopedLock locker(messageLock); - assertClusterSafe(); Message* msg; msg = messages->find(position); @@ -375,12 +362,13 @@ bool Queue::acquire(const QueueCursor& position, const std::string& consumer) bool Queue::getNextMessage(Message& m, Consumer::shared_ptr& c) { - checkNotDeleted(c); + if (!checkNotDeleted(c)) return false; QueueListeners::NotificationSet set; while (true) { //TODO: reduce lock scope Mutex::ScopedLock locker(messageLock); - Message* msg = messages->next(*c); + QueueCursor cursor = c->getCursor(); // Save current position. + Message* msg = messages->next(*c); // Advances c. if (msg) { if (msg->hasExpired()) { QPID_LOG(debug, "Message expired from queue '" << name << "'"); @@ -419,6 +407,7 @@ bool Queue::getNextMessage(Message& m, Consumer::shared_ptr& c) } else { //message(s) are available but consumer hasn't got enough credit QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); + c->setCursor(cursor); // Restore cursor, will try again with credit if (c->preAcquires()) { //let someone else try listeners.populate(set); @@ -480,7 +469,6 @@ bool Queue::find(SequenceNumber pos, Message& msg) const void Queue::consume(Consumer::shared_ptr c, bool requestExclusive) { - assertClusterSafe(); { Mutex::ScopedLock locker(messageLock); // NOTE: consumerCount is actually a count of all @@ -488,6 +476,11 @@ void Queue::consume(Consumer::shared_ptr c, bool requestExclusive) // Check for exclusivity of acquiring consumers. size_t acquiringConsumers = consumerCount - browserCount; if (c->preAcquires()) { + if(settings.isBrowseOnly) { + throw NotAllowedException( + QPID_MSG("Queue " << name << " is browse only. Refusing acquiring consumer.")); + } + if(exclusive) { throw ResourceLockedException( QPID_MSG("Queue " << getName() @@ -502,22 +495,29 @@ void Queue::consume(Consumer::shared_ptr c, bool requestExclusive) } } } - else + else if(c->isCounted()) { browserCount++; - consumerCount++; - //reset auto deletion timer if necessary - if (settings.autoDeleteDelay && autoDeleteTask) { - autoDeleteTask->cancel(); } - observeConsumerAdd(*c, locker); + if(c->isCounted()) { + consumerCount++; + + //reset auto deletion timer if necessary + if (settings.autoDeleteDelay && autoDeleteTask) { + autoDeleteTask->cancel(); + } + + observeConsumerAdd(*c, locker); + } + } + if (mgmtObject != 0 && c->isCounted()) { + mgmtObject->inc_consumerCount(); } - if (mgmtObject != 0) - mgmtObject->inc_consumerCount (); } void Queue::cancel(Consumer::shared_ptr c) { removeListener(c); + if(c->isCounted()) { Mutex::ScopedLock locker(messageLock); consumerCount--; @@ -525,8 +525,9 @@ void Queue::cancel(Consumer::shared_ptr c) if(exclusive) exclusive = 0; observeConsumerRemove(*c, locker); } - if (mgmtObject != 0) - mgmtObject->dec_consumerCount (); + if (mgmtObject != 0 && c->isCounted()) { + mgmtObject->dec_consumerCount(); + } } /** @@ -733,7 +734,6 @@ uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty, void Queue::push(Message& message, bool /*isRecovery*/) { - assertClusterSafe(); QueueListeners::NotificationSet copy; { Mutex::ScopedLock locker(messageLock); @@ -1101,8 +1101,16 @@ void Queue::destroyed() notifyDeleted(); { Mutex::ScopedLock lock(messageLock); + for_each(observers.begin(), observers.end(), + boost::bind(&QueueObserver::destroy, _1)); observers.clear(); } + + if (mgmtObject != 0) { + mgmtObject->resourceDestroy(); + if (brokerMgmtObject) + brokerMgmtObject->dec_queueCount(); + } } void Queue::notifyDeleted() @@ -1136,7 +1144,7 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const { if (mgmtObject != 0 && persistenceId == 0 && externalQueueStore) { - ManagementObject* childObj = externalQueueStore->GetManagementObject(); + ManagementObject::shared_ptr childObj = externalQueueStore->GetManagementObject(); if (childObj != 0) childObj->setReference(mgmtObject->getObjectId()); } @@ -1180,6 +1188,7 @@ Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer ) void Queue::setAlternateExchange(boost::shared_ptr<Exchange> exchange) { alternateExchange = exchange; + alternateExchange->incAlternateUsers(); if (mgmtObject) { if (exchange.get() != 0) mgmtObject->set_altExchange(exchange->GetManagementObject()->getObjectId()); @@ -1197,14 +1206,10 @@ void tryAutoDeleteImpl(Broker& broker, Queue::shared_ptr queue, const std::strin { if (broker.getQueues().destroyIf(queue->getName(), boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) { - QPID_LOG(debug, "Auto-deleting " << queue->getName()); - queue->destroyed(); - - if (broker.getManagementAgent()) - broker.getManagementAgent()->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, queue->getName())); - QPID_LOG_CAT(debug, model, "Delete queue. name:" << queue->getName() + QPID_LOG_CAT(debug, model, "Auto-delete queue: " << queue->getName() << " user:" << userId << " rhost:" << connectionId ); + queue->destroyed(); } } @@ -1233,7 +1238,7 @@ void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue, const std::st if (queue->settings.autoDeleteDelay && queue->canAutoDelete()) { AbsTime time(now(), Duration(queue->settings.autoDeleteDelay * TIME_SEC)); queue->autoDeleteTask = boost::intrusive_ptr<qpid::sys::TimerTask>(new AutoDeleteTask(broker, queue, connectionId, userId, time)); - broker.getClusterTimer().add(queue->autoDeleteTask); + broker.getTimer().add(queue->autoDeleteTask); QPID_LOG(debug, "Timed auto-delete for " << queue->getName() << " initiated"); } else { tryAutoDeleteImpl(broker, queue, connectionId, userId); @@ -1290,7 +1295,7 @@ void Queue::setExternalQueueStore(ExternalQueueStore* inst) { externalQueueStore = inst; if (inst) { - ManagementObject* childObj = inst->GetManagementObject(); + ManagementObject::shared_ptr childObj = inst->GetManagementObject(); if (childObj != 0 && mgmtObject != 0) childObj->setReference(mgmtObject->getObjectId()); } @@ -1378,9 +1383,9 @@ void Queue::countLoadedFromDisk(uint64_t size) const } -ManagementObject* Queue::GetManagementObject (void) const +ManagementObject::shared_ptr Queue::GetManagementObject(void) const { - return (ManagementObject*) mgmtObject; + return mgmtObject; } Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, string& etext) @@ -1459,6 +1464,16 @@ SequenceNumber Queue::getPosition() { return sequence; } +void Queue::getRange(framing::SequenceNumber& front, framing::SequenceNumber& back, + SubscriptionType type) +{ + Mutex::ScopedLock locker(messageLock); + QueueCursor cursor(type); + back = sequence; + Message* message = messages->next(cursor); + front = message ? message->getSequence() : back+1; +} + int Queue::getEventMode() { return eventMode; } void Queue::recoveryComplete(ExchangeRegistry& exchanges) @@ -1493,20 +1508,11 @@ void Queue::observeEnqueue(const Message& m, const Mutex::ScopedLock&) mgntEnqStats(m, mgmtObject, brokerMgmtObject); } -// Note: accessing listeners outside of lock is dangerous. Caller must ensure the queue's -// state is not changed while listeners is referenced. -QueueListeners& Queue::getListeners() { return listeners; } - -// Note: accessing messages outside of lock is dangerous. Caller must ensure the queue's -// state is not changed while messages is referenced. -Messages& Queue::getMessages() { return *messages; } -const Messages& Queue::getMessages() const { return *messages; } - -void Queue::checkNotDeleted(const Consumer::shared_ptr& c) +bool Queue::checkNotDeleted(const Consumer::shared_ptr& c) { - if (deleted && !c->hideDeletedError()) { + if (deleted && !c->hideDeletedError()) throw ResourceDeletedException(QPID_MSG("Queue " << getName() << " has been deleted.")); - } + return !deleted; } void Queue::addObserver(boost::shared_ptr<QueueObserver> observer) @@ -1641,7 +1647,7 @@ Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {} bool Queue::UsageBarrier::acquire() { - Monitor::ScopedLock l(parent.messageLock); /** @todo: use a dedicated lock instead of messageLock */ + Monitor::ScopedLock l(usageLock); if (parent.deleted) { return false; } else { @@ -1652,15 +1658,20 @@ bool Queue::UsageBarrier::acquire() void Queue::UsageBarrier::release() { - Monitor::ScopedLock l(parent.messageLock); - if (--count == 0) parent.messageLock.notifyAll(); + Monitor::ScopedLock l(usageLock); + if (--count == 0) usageLock.notifyAll(); } void Queue::UsageBarrier::destroy() { - Monitor::ScopedLock l(parent.messageLock); + Monitor::ScopedLock l(usageLock); parent.deleted = true; - while (count) parent.messageLock.wait(); + while (count) usageLock.wait(); +} + +void Queue::addArgument(const string& key, const types::Variant& value) { + settings.original.insert(types::Variant::Map::value_type(key, value)); + if (mgmtObject != 0) mgmtObject->set_arguments(settings.asMap()); } }} diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h index 1294f813aa..bb713eba2b 100644 --- a/cpp/src/qpid/broker/Queue.h +++ b/cpp/src/qpid/broker/Queue.h @@ -38,7 +38,6 @@ #include "qpid/framing/SequenceNumber.h" #include "qpid/sys/AtomicValue.h" #include "qpid/sys/Monitor.h" -#include "qpid/sys/Timer.h" #include "qpid/management/Manageable.h" #include "qmf/org/apache/qpid/broker/Queue.h" #include "qmf/org/apache/qpid/broker/Broker.h" @@ -56,6 +55,9 @@ #include <algorithm> namespace qpid { +namespace sys { +class TimerTask; +} namespace broker { class Broker; class Exchange; @@ -83,6 +85,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, { Queue& parent; uint count; + qpid::sys::Monitor usageLock; UsageBarrier(Queue&); bool acquire(); @@ -142,18 +145,18 @@ class Queue : public boost::enable_shared_from_this<Queue>, * o consumerCount (TBD: move under separate lock) * o Queue::UsageBarrier (TBD: move under separate lock) */ - mutable qpid::sys::Monitor messageLock; + mutable qpid::sys::Mutex messageLock; mutable qpid::sys::Mutex ownershipLock; mutable uint64_t persistenceId; - const QueueSettings settings; + QueueSettings settings; qpid::framing::FieldTable encodableSettings; QueueDepth current; QueueBindings bindings; std::string alternateExchangeName; boost::shared_ptr<Exchange> alternateExchange; framing::SequenceNumber sequence; - qmf::org::apache::qpid::broker::Queue* mgmtObject; - qmf::org::apache::qpid::broker::Broker* brokerMgmtObject; + qmf::org::apache::qpid::broker::Queue::shared_ptr mgmtObject; + qmf::org::apache::qpid::broker::Broker::shared_ptr brokerMgmtObject; sys::AtomicValue<uint32_t> dequeueSincePurge; // Count dequeues since last purge. int eventMode; Observers observers; @@ -189,7 +192,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, int getEventMode(); void dequeueFromStore(boost::intrusive_ptr<PersistableMessage>); void abandoned(const Message& message); - void checkNotDeleted(const Consumer::shared_ptr&); + bool checkNotDeleted(const Consumer::shared_ptr&); void notifyDeleted(); uint32_t remove(uint32_t maxCount, MessagePredicate, MessageFunctor, SubscriptionType); virtual bool checkDepth(const QueueDepth& increment, const Message&); @@ -338,7 +341,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, * exclusive owner */ static Queue::shared_ptr restore(QueueRegistry& queues, framing::Buffer& buffer); - static void tryAutoDelete(Broker& broker, Queue::shared_ptr, const std::string& connectionId, const std::string& userId); + QPID_BROKER_EXTERN static void tryAutoDelete(Broker& broker, Queue::shared_ptr, const std::string& connectionId, const std::string& userId); virtual void setExternalQueueStore(ExternalQueueStore* inst); @@ -352,7 +355,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, QPID_BROKER_EXTERN void countLoadedFromDisk(uint64_t size) const; // Manageable entry points - QPID_BROKER_EXTERN management::ManagementObject* GetManagementObject (void) const; + QPID_BROKER_EXTERN management::ManagementObject::shared_ptr GetManagementObject(void) const; management::Manageable::status_t QPID_BROKER_EXTERN ManagementMethod (uint32_t methodId, management::Args& args, std::string& text); QPID_BROKER_EXTERN void query(::qpid::types::Variant::Map&) const; @@ -382,15 +385,30 @@ class Queue : public boost::enable_shared_from_this<Queue>, * * The _caller_ must ensure that any messages after pos have been dequeued. * - * Used by HA/cluster code for queue replication. + * Used by HA code for queue replication. */ QPID_BROKER_EXTERN void setPosition(framing::SequenceNumber pos); /** *@return sequence number for the back of the queue. The next message pushed - * will be at getPosition+1 + * will be at getPosition()+1 */ QPID_BROKER_EXTERN framing::SequenceNumber getPosition(); + + /** + * Set front and back. + * If the queue is empty then front = back+1 (the first message to + * consume will be the next message pushed.) + * + *@param front = Position of first message to consume. + *@param back = getPosition(), next message pushed will be getPosition()+1 + *@param type Subscription type to use to determine the front. + */ + QPID_BROKER_EXTERN void getRange( + framing::SequenceNumber& front, framing::SequenceNumber& back, + SubscriptionType type=CONSUMER + ); + QPID_BROKER_EXTERN void addObserver(boost::shared_ptr<QueueObserver>); QPID_BROKER_EXTERN void removeObserver(boost::shared_ptr<QueueObserver>); QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key); @@ -399,11 +417,6 @@ class Queue : public boost::enable_shared_from_this<Queue>, */ QPID_BROKER_EXTERN void recoveryComplete(ExchangeRegistry& exchanges); - // For cluster update - QPID_BROKER_EXTERN QueueListeners& getListeners(); - QPID_BROKER_EXTERN Messages& getMessages(); - QPID_BROKER_EXTERN const Messages& getMessages() const; - /** * Reserve space in policy for an enqueued message that * has been recovered in the prepared state (dtx only) @@ -420,6 +433,10 @@ class Queue : public boost::enable_shared_from_this<Queue>, uint32_t getDequeueSincePurge() { return dequeueSincePurge.get(); } QPID_BROKER_EXTERN void setDequeueSincePurge(uint32_t value); + + /** Add an argument to be included in management messages about this queue. */ + QPID_BROKER_EXTERN void addArgument(const std::string& key, const types::Variant& value); + friend class QueueFactory; }; } diff --git a/cpp/src/qpid/broker/QueueCleaner.cpp b/cpp/src/qpid/broker/QueueCleaner.cpp index 838bc28be8..8d9e3f43dd 100644 --- a/cpp/src/qpid/broker/QueueCleaner.cpp +++ b/cpp/src/qpid/broker/QueueCleaner.cpp @@ -18,15 +18,36 @@ * under the License. * */ -#include "qpid/broker/Queue.h" #include "qpid/broker/QueueCleaner.h" #include "qpid/broker/Broker.h" +#include "qpid/broker/Queue.h" +#include "qpid/sys/Timer.h" + +#include <boost/function.hpp> #include <boost/bind.hpp> namespace qpid { namespace broker { +namespace { + typedef boost::function0<void> FireFunction; + class Task : public sys::TimerTask + { + public: + Task(FireFunction f, sys::Duration duration); + void fire(); + private: + FireFunction fireFunction; + }; + + Task::Task(FireFunction f, qpid::sys::Duration d) : sys::TimerTask(d,"QueueCleaner"), fireFunction(f) {} + + void Task::fire() + { + fireFunction(); + } +} QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer* t) : queues(q), timer(t) {} QueueCleaner::~QueueCleaner() @@ -37,7 +58,7 @@ QueueCleaner::~QueueCleaner() void QueueCleaner::start(qpid::sys::Duration p) { period = p; - task = new Task(*this, p); + task = new Task(boost::bind(&QueueCleaner::fired, this), p); timer->add(task); } @@ -45,14 +66,6 @@ void QueueCleaner::setTimer(qpid::sys::Timer* timer) { this->timer = timer; } - -QueueCleaner::Task::Task(QueueCleaner& p, qpid::sys::Duration d) : sys::TimerTask(d,"QueueCleaner"), parent(p) {} - -void QueueCleaner::Task::fire() -{ - parent.fired(); -} - namespace { struct CollectQueues { diff --git a/cpp/src/qpid/broker/QueueCleaner.h b/cpp/src/qpid/broker/QueueCleaner.h index ffebfe3e1b..896af1dcd5 100644 --- a/cpp/src/qpid/broker/QueueCleaner.h +++ b/cpp/src/qpid/broker/QueueCleaner.h @@ -23,9 +23,17 @@ */ #include "qpid/broker/BrokerImportExport.h" -#include "qpid/sys/Timer.h" +#include "qpid/sys/Time.h" + +#include <boost/intrusive_ptr.hpp> namespace qpid { + +namespace sys { + class Timer; + class TimerTask; +} + namespace broker { class QueueRegistry; @@ -39,16 +47,8 @@ class QueueCleaner QPID_BROKER_EXTERN ~QueueCleaner(); QPID_BROKER_EXTERN void start(sys::Duration period); QPID_BROKER_EXTERN void setTimer(sys::Timer* timer); - private: - class Task : public sys::TimerTask - { - public: - Task(QueueCleaner& parent, sys::Duration duration); - void fire(); - private: - QueueCleaner& parent; - }; + private: boost::intrusive_ptr<sys::TimerTask> task; QueueRegistry& queues; sys::Timer* timer; diff --git a/cpp/src/qpid/broker/QueueFlowLimit.cpp b/cpp/src/qpid/broker/QueueFlowLimit.cpp index 11b9cbae63..9b2e31c925 100644 --- a/cpp/src/qpid/broker/QueueFlowLimit.cpp +++ b/cpp/src/qpid/broker/QueueFlowLimit.cpp @@ -29,7 +29,6 @@ #include "qpid/log/Statement.h" #include "qpid/sys/Mutex.h" #include "qpid/broker/SessionState.h" -#include "qpid/sys/ClusterSafe.h" #include "qmf/org/apache/qpid/broker/Queue.h" @@ -66,10 +65,10 @@ namespace { QueueFlowLimit::QueueFlowLimit(Queue *_queue, uint32_t _flowStopCount, uint32_t _flowResumeCount, uint64_t _flowStopSize, uint64_t _flowResumeSize) - : StatefulQueueObserver(std::string("QueueFlowLimit")), queue(_queue), queueName("<unknown>"), + : queue(_queue), queueName("<unknown>"), flowStopCount(_flowStopCount), flowResumeCount(_flowResumeCount), flowStopSize(_flowStopSize), flowResumeSize(_flowResumeSize), - flowStopped(false), count(0), size(0), queueMgmtObj(0), broker(0) + flowStopped(false), count(0), size(0), broker(0) { uint32_t maxCount(0); uint64_t maxSize(0); @@ -79,7 +78,7 @@ QueueFlowLimit::QueueFlowLimit(Queue *_queue, if (queue->getSettings().maxDepth.hasCount()) maxCount = queue->getSettings().maxDepth.getCount(); if (queue->getSettings().maxDepth.hasCount()) maxSize = queue->getSettings().maxDepth.getSize(); broker = queue->getBroker(); - queueMgmtObj = dynamic_cast<_qmfBroker::Queue*> (queue->GetManagementObject()); + queueMgmtObj = boost::dynamic_pointer_cast<_qmfBroker::Queue> (queue->GetManagementObject()); if (queueMgmtObj) { queueMgmtObj->set_flowStopped(isFlowControlActive()); } @@ -130,11 +129,6 @@ void QueueFlowLimit::enqueued(const Message& msg) } if (flowStopped || !index.empty()) { - // ignore flow control if we are populating the queue due to cluster replication: - if (broker && broker->isClusterUpdatee()) { - QPID_LOG(trace, "Queue \"" << queueName << "\": ignoring flow control for msg pos=" << msg.getSequence()); - return; - } QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.getSequence()); msg.getPersistentContext()->getIngressCompletion().startCompleter(); // don't complete until flow resumes bool unique; @@ -297,79 +291,8 @@ QueueFlowLimit *QueueFlowLimit::createLimit(Queue *queue, const QueueSettings& s return 0; } -/* Cluster replication */ - -namespace { - /** pack a set of sequence number ranges into a framing::Array */ - void buildSeqRangeArray(qpid::framing::Array *seqs, - const qpid::framing::SequenceNumber& first, - const qpid::framing::SequenceNumber& last) - { - seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(first))); - seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(last))); - } -} - -/** Runs on UPDATER to snapshot current state */ -void QueueFlowLimit::getState(qpid::framing::FieldTable& state ) const -{ - sys::Mutex::ScopedLock l(indexLock); - state.clear(); - - framing::SequenceSet ss; - if (!index.empty()) { - /* replicate the set of messages pending flow control */ - for (std::map<framing::SequenceNumber, Message >::const_iterator itr = index.begin(); - itr != index.end(); ++itr) { - ss.add(itr->first); - } - framing::Array seqs(TYPE_CODE_UINT32); - typedef boost::function<void(framing::SequenceNumber, framing::SequenceNumber)> arrayBuilder; - ss.for_each((arrayBuilder)boost::bind(&buildSeqRangeArray, &seqs, _1, _2)); - state.setArray("pendingMsgSeqs", seqs); - } - QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicating pending msgs, range=" << ss); -} - - -/** called on UPDATEE to set state from snapshot */ -void QueueFlowLimit::setState(const qpid::framing::FieldTable& state) -{ - sys::Mutex::ScopedLock l(indexLock); - index.clear(); - - framing::SequenceSet fcmsg; - framing::Array seqArray(TYPE_CODE_UINT32); - if (state.getArray("pendingMsgSeqs", seqArray)) { - assert((seqArray.count() & 0x01) == 0); // must be even since they are sequence ranges - framing::Array::const_iterator i = seqArray.begin(); - while (i != seqArray.end()) { - framing::SequenceNumber first((*i)->getIntegerValue<uint32_t, 4>()); - ++i; - framing::SequenceNumber last((*i)->getIntegerValue<uint32_t, 4>()); - ++i; - fcmsg.add(first, last); - for (SequenceNumber seq = first; seq <= last; ++seq) { - Message msg; - queue->find(seq, msg); // fyi: may not be found if msg is acquired & unacked - bool unique; - unique = index.insert(std::pair<framing::SequenceNumber, Message >(seq, msg)).second; - // Like this to avoid tripping up unused variable warning when NDEBUG set - if (!unique) assert(unique); - } - } - } - - flowStopped = index.size() != 0; - if (queueMgmtObj) { - queueMgmtObj->set_flowStopped(isFlowControlActive()); - } - QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicated the pending msgs, range=" << fcmsg) -} - - namespace qpid { - namespace broker { +namespace broker { std::ostream& operator<<(std::ostream& out, const QueueFlowLimit& f) { @@ -378,6 +301,6 @@ std::ostream& operator<<(std::ostream& out, const QueueFlowLimit& f) return out; } - } +} } diff --git a/cpp/src/qpid/broker/QueueFlowLimit.h b/cpp/src/qpid/broker/QueueFlowLimit.h index 1bcc388ceb..b9aa09ec3a 100644 --- a/cpp/src/qpid/broker/QueueFlowLimit.h +++ b/cpp/src/qpid/broker/QueueFlowLimit.h @@ -26,19 +26,13 @@ #include <iostream> #include <memory> #include "qpid/broker/BrokerImportExport.h" -#include "qpid/broker/StatefulQueueObserver.h" +#include "qpid/broker/QueueObserver.h" #include "qpid/framing/FieldTable.h" #include "qpid/framing/SequenceNumber.h" #include "qpid/sys/AtomicValue.h" #include "qpid/sys/Mutex.h" +#include "qmf/org/apache/qpid/broker/Queue.h" -namespace qmf { -namespace org { -namespace apache { -namespace qpid { -namespace broker { - class Queue; -}}}}} namespace _qmfBroker = qmf::org::apache::qpid::broker; namespace qpid { @@ -46,6 +40,7 @@ namespace broker { class Broker; class Queue; +class Message; struct QueueSettings; /** @@ -55,7 +50,7 @@ struct QueueSettings; * passing _either_ level may turn flow control ON, but _both_ must be * below level before flow control will be turned OFF. */ - class QueueFlowLimit : public StatefulQueueObserver + class QueueFlowLimit : public QueueObserver { static uint64_t defaultMaxSize; static uint defaultFlowStopRatio; @@ -90,10 +85,6 @@ struct QueueSettings; QPID_BROKER_EXTERN void acquired(const Message&) {}; QPID_BROKER_EXTERN void requeued(const Message&) {}; - /** for clustering: */ - QPID_BROKER_EXTERN void getState(qpid::framing::FieldTable&) const; - QPID_BROKER_EXTERN void setState(const qpid::framing::FieldTable&); - uint32_t getFlowStopCount() const { return flowStopCount; } uint32_t getFlowResumeCount() const { return flowResumeCount; } uint64_t getFlowStopSize() const { return flowStopSize; } @@ -118,7 +109,7 @@ struct QueueSettings; std::map<framing::SequenceNumber, Message > index; mutable qpid::sys::Mutex indexLock; - _qmfBroker::Queue *queueMgmtObj; + _qmfBroker::Queue::shared_ptr queueMgmtObj; const Broker *broker; diff --git a/cpp/src/qpid/broker/QueueObserver.h b/cpp/src/qpid/broker/QueueObserver.h index 29e867253e..2ba98f6945 100644 --- a/cpp/src/qpid/broker/QueueObserver.h +++ b/cpp/src/qpid/broker/QueueObserver.h @@ -69,6 +69,7 @@ class QueueObserver virtual void requeued(const Message&) = 0; virtual void consumerAdded( const Consumer& ) {}; virtual void consumerRemoved( const Consumer& ) {}; + virtual void destroy() {}; private: }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp index eb525b6727..2fdcf3b8e6 100644 --- a/cpp/src/qpid/broker/QueueRegistry.cpp +++ b/cpp/src/qpid/broker/QueueRegistry.cpp @@ -23,10 +23,14 @@ #include "qpid/broker/QueueRegistry.h" #include "qpid/broker/Exchange.h" #include "qpid/log/Statement.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/framing/reply_exceptions.h" +#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h" +#include "qmf/org/apache/qpid/broker/EventQueueDelete.h" #include <sstream> #include <assert.h> +namespace _qmf = qmf::org::apache::qpid::broker; using namespace qpid::broker; using namespace qpid::sys; using std::string; @@ -44,7 +48,10 @@ QueueRegistry::declare(const string& name, const QueueSettings& settings, bool recovering/*true if this declare is a result of recovering queue definition from persistent - record*/) + record*/, + const OwnershipToken* owner, + std::string connectionId, + std::string userId) { std::pair<Queue::shared_ptr, bool> result; { @@ -53,25 +60,38 @@ QueueRegistry::declare(const string& name, const QueueSettings& settings, if (i == queues.end()) { Queue::shared_ptr queue = create(name, settings); //Move this to factory also? - if (alternate) { + if (alternate) queue->setAlternateExchange(alternate);//need to do this *before* create - alternate->incAlternateUsers(); - } if (!recovering) { //create persistent record if required queue->create(); } queues[name] = queue; + // NOTE: raiseEvent and queueCreate must be called with the lock held in + // order to ensure events are generated in the correct order. + // Call queueCreate before raiseEvents so it can add arguments that + // will be included in the management event. + if (getBroker()) getBroker()->getConfigurationObservers().queueCreate(queue); result = std::pair<Queue::shared_ptr, bool>(queue, true); } else { result = std::pair<Queue::shared_ptr, bool>(i->second, false); } + if (getBroker() && getBroker()->getManagementAgent()) { + getBroker()->getManagementAgent()->raiseEvent( + _qmf::EventQueueDeclare( + connectionId, userId, name, + settings.durable, owner, settings.autodelete, + alternate ? alternate->getName() : string(), + result.first->getSettings().asMap(), + result.second ? "created" : "existing")); + } } - if (getBroker() && result.second) getBroker()->getConfigurationObservers().queueCreate(result.first); return result; } -void QueueRegistry::destroy(const string& name) { +void QueueRegistry::destroy( + const string& name, const string& connectionId, const string& userId) +{ Queue::shared_ptr q; { qpid::sys::RWlock::ScopedWlock locker(lock); @@ -79,9 +99,17 @@ void QueueRegistry::destroy(const string& name) { if (i != queues.end()) { q = i->second; queues.erase(i); + if (getBroker()) { + // NOTE: queueDestroy and raiseEvent must be called with the + // lock held in order to ensure events are generated + // in the correct order. + getBroker()->getConfigurationObservers().queueDestroy(q); + if (getBroker()->getManagementAgent()) + getBroker()->getManagementAgent()->raiseEvent( + _qmf::EventQueueDelete(connectionId, userId, name)); + } } } - if (getBroker() && q) getBroker()->getConfigurationObservers().queueDestroy(q); } Queue::shared_ptr QueueRegistry::find(const string& name){ diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h index ada76f9cca..0170c441b3 100644 --- a/cpp/src/qpid/broker/QueueRegistry.h +++ b/cpp/src/qpid/broker/QueueRegistry.h @@ -59,7 +59,9 @@ class QueueRegistry : QueueFactory { const std::string& name, const QueueSettings& settings, boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(), - bool recovering = false); + bool recovering = false, + const OwnershipToken* owner = 0, + std::string connectionId=std::string(), std::string userId=std::string()); /** * Destroy the named queue. @@ -73,7 +75,11 @@ class QueueRegistry : QueueFactory { * subsequent calls to find or declare with the same name. * */ - QPID_BROKER_EXTERN void destroy(const std::string& name); + QPID_BROKER_EXTERN void destroy( + const std::string& name, + const std::string& connectionId=std::string(), + const std::string& userId=std::string()); + template <class Test> bool destroyIf(const std::string& name, Test test) { if (test()) { diff --git a/cpp/src/qpid/broker/QueueSettings.cpp b/cpp/src/qpid/broker/QueueSettings.cpp index 91616636f1..b92a81bcf3 100644 --- a/cpp/src/qpid/broker/QueueSettings.cpp +++ b/cpp/src/qpid/broker/QueueSettings.cpp @@ -33,10 +33,13 @@ namespace broker { namespace { const std::string MAX_COUNT("qpid.max_count"); const std::string MAX_SIZE("qpid.max_size"); +const std::string MAX_FILE_COUNT("qpid.file_count"); +const std::string MAX_FILE_SIZE("qpid.file_size"); const std::string POLICY_TYPE("qpid.policy_type"); const std::string POLICY_TYPE_REJECT("reject"); const std::string POLICY_TYPE_RING("ring"); const std::string NO_LOCAL("no-local"); +const std::string BROWSE_ONLY("qpid.browse-only"); const std::string TRACE_ID("qpid.trace.id"); const std::string TRACE_EXCLUDES("qpid.trace.exclude"); const std::string LVQ_KEY("qpid.last_value_queue_key"); @@ -74,12 +77,14 @@ const QueueSettings::Aliases QueueSettings::aliases; QueueSettings::QueueSettings(bool d, bool a) : durable(d), autodelete(a), + isTemporary(false), priorities(0), defaultFairshare(0), shareGroups(false), addTimestamp(false), dropMessagesAtLimit(false), noLocal(false), + isBrowseOnly(false), autoDeleteDelay(0), alertRepeatInterval(60) {} @@ -106,6 +111,9 @@ bool QueueSettings::handle(const std::string& key, const qpid::types::Variant& v } else if (key == NO_LOCAL) { noLocal = value; return true; + } else if (key == BROWSE_ONLY) { + isBrowseOnly = value; + return true; } else if (key == TRACE_ID) { traceId = value.asString(); return true; @@ -163,6 +171,12 @@ bool QueueSettings::handle(const std::string& key, const qpid::types::Variant& v } else if (key == ALERT_SIZE) { alertThreshold.setSize(value); return true; + } else if (key == MAX_FILE_COUNT && value.asUint64() > 0) { + maxFileCount = value.asUint64(); + return false; // 'handle' here and also pass to store + } else if (key == MAX_FILE_SIZE && value.asUint64() > 0) { + maxFileSize = value.asUint64(); + return false; // 'handle' here and also pass to store } else { return false; } diff --git a/cpp/src/qpid/broker/QueueSettings.h b/cpp/src/qpid/broker/QueueSettings.h index 2443624615..62d34db5cb 100644 --- a/cpp/src/qpid/broker/QueueSettings.h +++ b/cpp/src/qpid/broker/QueueSettings.h @@ -43,6 +43,7 @@ struct QueueSettings bool durable; bool autodelete; + bool isTemporary; //basic queue types: std::string lvqKey; @@ -59,6 +60,7 @@ struct QueueSettings bool dropMessagesAtLimit;//aka ring queue policy bool noLocal; + bool isBrowseOnly; std::string traceId; std::string traceExcludes; uint64_t autoDeleteDelay;//queueTtl? @@ -71,6 +73,10 @@ struct QueueSettings QueueDepth alertThreshold; int64_t alertRepeatInterval; + //file limits checked by Acl and shared with storeSettings + uint64_t maxFileSize; + uint64_t maxFileCount; + //yuck, yuck qpid::framing::FieldTable storeSettings; std::map<std::string, qpid::types::Variant> original; diff --git a/cpp/src/qpid/broker/RecoverableExchange.h b/cpp/src/qpid/broker/RecoverableExchange.h index 6bda1e2617..2302a7d925 100644 --- a/cpp/src/qpid/broker/RecoverableExchange.h +++ b/cpp/src/qpid/broker/RecoverableExchange.h @@ -45,6 +45,9 @@ public: const std::string& routingKey, qpid::framing::FieldTable&, AsyncStore* const store) = 0; + + virtual std::string getName() const = 0; + virtual ~RecoverableExchange() {}; }; diff --git a/cpp/src/qpid/broker/RecoverableMessage.h b/cpp/src/qpid/broker/RecoverableMessage.h index c98857ceb0..aafcd756d5 100644 --- a/cpp/src/qpid/broker/RecoverableMessage.h +++ b/cpp/src/qpid/broker/RecoverableMessage.h @@ -22,12 +22,14 @@ * */ +#include <boost/intrusive_ptr.hpp> #include <boost/shared_ptr.hpp> #include "qpid/framing/amqp_types.h" #include "qpid/framing/Buffer.h" namespace qpid { namespace broker { +class ExpiryPolicy; /** * The interface through which messages are reloaded on recovery. @@ -38,6 +40,7 @@ public: typedef boost::shared_ptr<RecoverableMessage> shared_ptr; virtual void setPersistenceId(uint64_t id) = 0; virtual void setRedelivered() = 0; + virtual void computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e) = 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/RecoverableMessageImpl.h b/cpp/src/qpid/broker/RecoverableMessageImpl.h new file mode 100644 index 0000000000..a46f5a3676 --- /dev/null +++ b/cpp/src/qpid/broker/RecoverableMessageImpl.h @@ -0,0 +1,49 @@ +#ifndef QPID_BROKER_RECOVERABLEMESSAGEIMPL_H +#define QPID_BROKER_RECOVERABLEMESSAGEIMPL_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 "RecoverableMessage.h" + +namespace qpid { +namespace broker { +class DtxBuffer; +class Message; +class Queue; + +class RecoverableMessageImpl : public RecoverableMessage +{ + Message msg; +public: + RecoverableMessageImpl(const Message& _msg); + ~RecoverableMessageImpl() {}; + void setPersistenceId(uint64_t id); + void setRedelivered(); + void computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& ep); + bool loadContent(uint64_t available); + void decodeContent(framing::Buffer& buffer); + void recover(boost::shared_ptr<Queue> queue); + void enqueue(boost::shared_ptr<DtxBuffer> buffer, boost::shared_ptr<Queue> queue); + void dequeue(boost::shared_ptr<DtxBuffer> buffer, boost::shared_ptr<Queue> queue); +}; +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_RECOVERABLEMESSAGEIMPL_H*/ diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index f3e1639ca5..901b3ad15a 100644 --- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -25,6 +25,8 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/Link.h" #include "qpid/broker/Bridge.h" +#include "qpid/broker/Protocol.h" +#include "qpid/broker/RecoverableMessageImpl.h" #include "qpid/broker/RecoveredEnqueue.h" #include "qpid/broker/RecoveredDequeue.h" #include "qpid/broker/amqp_0_10/MessageTransfer.h" @@ -38,26 +40,11 @@ namespace qpid { namespace broker { RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, LinkRegistry& _links, - DtxManager& _dtxMgr) - : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr) {} + DtxManager& _dtxMgr, ProtocolRegistry& p) + : queues(_queues), exchanges(_exchanges), links(_links), dtxMgr(_dtxMgr), protocols(p) {} RecoveryManagerImpl::~RecoveryManagerImpl() {} -class RecoverableMessageImpl : public RecoverableMessage -{ - Message msg; -public: - RecoverableMessageImpl(const Message& _msg); - ~RecoverableMessageImpl() {}; - void setPersistenceId(uint64_t id); - void setRedelivered(); - bool loadContent(uint64_t available); - void decodeContent(framing::Buffer& buffer); - void recover(Queue::shared_ptr queue); - void enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue); - void dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue); -}; - class RecoverableQueueImpl : public RecoverableQueue { Queue::shared_ptr queue; @@ -82,6 +69,7 @@ public: RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {} void setPersistenceId(uint64_t id); void bind(const std::string& queue, const std::string& routingKey, qpid::framing::FieldTable& args, AsyncStore* const store); + string getName() const { return exchange->getName(); } }; class RecoverableConfigImpl : public RecoverableConfig @@ -130,10 +118,15 @@ RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer) { - //TODO: determine encoding/version actually used - boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer()); - transfer->decodeHeader(buffer); - return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer))); + framing::Buffer sniffer(buffer.getPointer(), buffer.available()); + RecoverableMessage::shared_ptr m = protocols.recover(sniffer); + if (m) { + return m; + } else { + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer()); + transfer->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer))); + } } RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid, @@ -193,6 +186,11 @@ void RecoverableMessageImpl::setRedelivered() msg.deliver();//increment delivery count (but at present that isn't recorded durably) } +void RecoverableMessageImpl::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& ep) +{ + msg.computeExpiration(ep); +} + void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg) { dynamic_pointer_cast<RecoverableMessageImpl>(msg)->recover(queue); diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.h b/cpp/src/qpid/broker/RecoveryManagerImpl.h index 7fca0be194..f824f4a540 100644 --- a/cpp/src/qpid/broker/RecoveryManagerImpl.h +++ b/cpp/src/qpid/broker/RecoveryManagerImpl.h @@ -30,15 +30,17 @@ namespace qpid { namespace broker { +class ProtocolRegistry; class RecoveryManagerImpl : public RecoveryManager{ QueueRegistry& queues; ExchangeRegistry& exchanges; LinkRegistry& links; DtxManager& dtxMgr; + ProtocolRegistry& protocols; public: RecoveryManagerImpl(QueueRegistry& queues, ExchangeRegistry& exchanges, LinkRegistry& links, - DtxManager& dtxMgr); + DtxManager& dtxMgr, ProtocolRegistry&); ~RecoveryManagerImpl(); RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer); diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp index 2d7c820b63..8211657e04 100644 --- a/cpp/src/qpid/broker/SaslAuthenticator.cpp +++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp @@ -24,6 +24,7 @@ #endif #include "qpid/broker/AclModule.h" +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" @@ -169,14 +170,8 @@ void SaslAuthenticator::fini(void) std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c ) { if (c.getBroker().getOptions().auth) { - // The cluster creates non-authenticated connections for internal shadow connections - // that are never connected to an external client. - if ( !c.isAuthenticated() ) - return std::auto_ptr<SaslAuthenticator>( - new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted)); - else - return std::auto_ptr<SaslAuthenticator>( - new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted)); + return std::auto_ptr<SaslAuthenticator>( + new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted)); } else { QPID_LOG(debug, "SASL: No Authentication Performed"); return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted)); @@ -424,7 +419,7 @@ void CyrusAuthenticator::start(const string& mechanism, const string* response) &challenge, &challenge_len); processAuthenticationStep(code, challenge, challenge_len); - qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject(); + qmf::org::apache::qpid::broker::Connection::shared_ptr cnxMgmt = connection.getMgmtObject(); if ( cnxMgmt ) cnxMgmt->set_saslMechanism(mechanism); } @@ -505,9 +500,9 @@ std::auto_ptr<SecurityLayer> CyrusAuthenticator::getSecurityLayer(uint16_t maxFr uint ssf = *(reinterpret_cast<const unsigned*>(value)); std::auto_ptr<SecurityLayer> securityLayer; if (ssf) { - securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize)); + securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize, ssf)); } - qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject(); + qmf::org::apache::qpid::broker::Connection::shared_ptr cnxMgmt = connection.getMgmtObject(); if ( cnxMgmt ) cnxMgmt->set_saslSsf(ssf); return securityLayer; diff --git a/cpp/src/qpid/broker/SecureConnection.cpp b/cpp/src/qpid/broker/SecureConnection.cpp index 5c1ebf3e8b..59ac9ef132 100644 --- a/cpp/src/qpid/broker/SecureConnection.cpp +++ b/cpp/src/qpid/broker/SecureConnection.cpp @@ -43,7 +43,7 @@ size_t SecureConnection::decode(const char* buffer, size_t size) } } -size_t SecureConnection::encode(const char* buffer, size_t size) +size_t SecureConnection::encode(char* buffer, size_t size) { if (secured) { return securityLayer->encode(buffer, size); diff --git a/cpp/src/qpid/broker/SecureConnection.h b/cpp/src/qpid/broker/SecureConnection.h index 1547faae1e..632087350e 100644 --- a/cpp/src/qpid/broker/SecureConnection.h +++ b/cpp/src/qpid/broker/SecureConnection.h @@ -43,7 +43,7 @@ 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); + size_t encode(char* buffer, size_t size); bool canEncode(); void closed(); bool isClosed() const; diff --git a/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/cpp/src/qpid/broker/SecureConnectionFactory.cpp index 757f6efc59..7bc2c94d1c 100644 --- a/cpp/src/qpid/broker/SecureConnectionFactory.cpp +++ b/cpp/src/qpid/broker/SecureConnectionFactory.cpp @@ -19,19 +19,21 @@ * */ #include "qpid/broker/SecureConnectionFactory.h" -#include "qpid/framing/ProtocolVersion.h" + #include "qpid/amqp_0_10/Connection.h" +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" #include "qpid/broker/SecureConnection.h" -#include "qpid/sys/SecuritySettings.h" +#include "qpid/framing/ProtocolVersion.h" #include "qpid/log/Statement.h" +#include "qpid/sys/SecuritySettings.h" namespace qpid { namespace broker { using framing::ProtocolVersion; using qpid::sys::SecuritySettings; -typedef std::auto_ptr<amqp_0_10::Connection> CodecPtr; +typedef std::auto_ptr<qpid::amqp_0_10::Connection> CodecPtr; typedef std::auto_ptr<SecureConnection> SecureConnectionPtr; typedef std::auto_ptr<Connection> ConnectionPtr; typedef std::auto_ptr<sys::ConnectionInputHandler> InputPtr; @@ -43,12 +45,14 @@ SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, cons const SecuritySettings& external) { if (v == ProtocolVersion(0, 10)) { SecureConnectionPtr sc(new SecureConnection()); - CodecPtr c(new amqp_0_10::Connection(out, id, false)); + CodecPtr c(new qpid::amqp_0_10::Connection(out, id, false)); ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, false)); i->setSecureConnection(sc.get()); c->setInputHandler(InputPtr(i.release())); sc->setCodec(std::auto_ptr<sys::ConnectionCodec>(c)); return sc.release(); + } else { + return broker.getProtocolRegistry().create(v, out, id, external); } return 0; } @@ -58,7 +62,7 @@ SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id, const SecuritySettings& external) { // used to create connections from one broker to another SecureConnectionPtr sc(new SecureConnection()); - CodecPtr c(new amqp_0_10::Connection(out, id, true)); + CodecPtr c(new qpid::amqp_0_10::Connection(out, id, true)); ConnectionPtr i(new broker::Connection(c.get(), broker, id, external, true )); i->setSecureConnection(sc.get()); c->setInputHandler(InputPtr(i.release())); diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp index 5fc9a1a932..751b3ff709 100644 --- a/cpp/src/qpid/broker/SemanticState.cpp +++ b/cpp/src/qpid/broker/SemanticState.cpp @@ -20,6 +20,8 @@ */ #include "qpid/broker/SessionState.h" + +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" #include "qpid/broker/DeliverableMessage.h" #include "qpid/broker/DtxAck.h" @@ -35,12 +37,14 @@ #include "qpid/framing/SequenceSet.h" #include "qpid/framing/IsInSequenceSet.h" #include "qpid/log/Statement.h" -#include "qpid/sys/ClusterSafe.h" +#include "qpid/management/ManagementAgent.h" #include "qpid/ptr_map.h" #include "qpid/broker/AclModule.h" +#include "qpid/broker/FedOps.h" #include <boost/bind.hpp> #include <boost/format.hpp> +#include <boost/tuple/tuple_comparison.hpp> #include <iostream> #include <sstream> @@ -49,6 +53,11 @@ #include <assert.h> +namespace { +const std::string X_SCOPE("x-scope"); +const std::string SESSION("session"); +} + namespace qpid { namespace broker { @@ -88,6 +97,7 @@ void SemanticState::closed() { if (dtxBuffer.get()) { dtxBuffer->fail(); } + unbindSessionBindings(); requeue(); //now unsubscribe, which may trigger queue deletion and thus @@ -277,7 +287,7 @@ void SemanticState::record(const DeliveryRecord& delivery) const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency"); -SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, +SemanticStateConsumerImpl::SemanticStateConsumerImpl(SemanticState* _parent, const string& _name, Queue::shared_ptr _queue, bool ack, @@ -303,7 +313,7 @@ Consumer(_name, type), notifyEnabled(true), syncFrequency(_arguments.getAsInt(QPID_SYNC_FREQUENCY)), deliveryCount(0), - mgmtObject(0) + protocols(parent->getSession().getBroker().getProtocolRegistry()) { if (parent != 0 && queue.get() != 0 && queue->GetManagementObject() !=0) { @@ -312,20 +322,20 @@ Consumer(_name, type), if (agent != 0) { - mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId(), getTag(), - !acquire, ackExpected, exclusive, ManagementAgent::toMap(arguments)); + mgmtObject = _qmf::Subscription::shared_ptr(new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId(), getTag(), + !acquire, ackExpected, exclusive, ManagementAgent::toMap(arguments))); agent->addObject (mgmtObject); mgmtObject->set_creditMode("WINDOW"); } } } -ManagementObject* SemanticState::ConsumerImpl::GetManagementObject (void) const +ManagementObject::shared_ptr SemanticStateConsumerImpl::GetManagementObject (void) const { - return (ManagementObject*) mgmtObject; + return mgmtObject; } -Manageable::status_t SemanticState::ConsumerImpl::ManagementMethod (uint32_t methodId, Args&, string&) +Manageable::status_t SemanticStateConsumerImpl::ManagementMethod (uint32_t methodId, Args&, string&) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; @@ -335,24 +345,23 @@ Manageable::status_t SemanticState::ConsumerImpl::ManagementMethod (uint32_t met } -OwnershipToken* SemanticState::ConsumerImpl::getSession() +OwnershipToken* SemanticStateConsumerImpl::getSession() { return &(parent->session); } -bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg) +bool SemanticStateConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg) { return deliver(cursor, msg, shared_from_this()); } -bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg, boost::shared_ptr<Consumer> consumer) +bool SemanticStateConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg, boost::shared_ptr<Consumer> consumer) { - assertClusterSafe(); allocateCredit(msg); + boost::intrusive_ptr<const amqp_0_10::MessageTransfer> transfer = protocols.translate(msg); DeliveryRecord record(cursor, msg.getSequence(), queue, getTag(), - consumer, acquire, !ackExpected, credit.isWindowMode(), amqp_0_10::MessageTransfer::getRequiredCredit(msg)); + consumer, acquire, !ackExpected, credit.isWindowMode(), transfer->getRequiredCredit()); bool sync = syncFrequency && ++deliveryCount >= syncFrequency; if (sync) deliveryCount = 0;//reset - const amqp_0_10::MessageTransfer* transfer = dynamic_cast<const amqp_0_10::MessageTransfer*>(&msg.getEncoding()); record.setId(parent->session.deliver(*transfer, getTag(), msg.isRedelivered(), msg.getTtl(), msg.getTimestamp(), ackExpected ? message::ACCEPT_MODE_EXPLICIT : message::ACCEPT_MODE_NONE, @@ -370,27 +379,26 @@ bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Messa return true; } -bool SemanticState::ConsumerImpl::filter(const Message&) +bool SemanticStateConsumerImpl::filter(const Message&) { return true; } -bool SemanticState::ConsumerImpl::accept(const Message& msg) +bool SemanticStateConsumerImpl::accept(const Message& msg) { - assertClusterSafe(); // TODO 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)); + blocked = !checkCredit(msg); return !blocked; } namespace { struct ConsumerName { - const SemanticState::ConsumerImpl& consumer; - ConsumerName(const SemanticState::ConsumerImpl& ci) : consumer(ci) {} + const SemanticStateConsumerImpl& consumer; + ConsumerName(const SemanticStateConsumerImpl& ci) : consumer(ci) {} }; ostream& operator<<(ostream& o, const ConsumerName& pc) { @@ -399,26 +407,27 @@ ostream& operator<<(ostream& o, const ConsumerName& pc) { } } -void SemanticState::ConsumerImpl::allocateCredit(const Message& msg) +void SemanticStateConsumerImpl::allocateCredit(const Message& msg) { - assertClusterSafe(); Credit original = credit; - credit.consume(1, qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg)); + boost::intrusive_ptr<const amqp_0_10::MessageTransfer> transfer = protocols.translate(msg); + credit.consume(1, transfer->getRequiredCredit()); QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this) << ", was " << original << " now " << credit); } -bool SemanticState::ConsumerImpl::checkCredit(const Message& msg) +bool SemanticStateConsumerImpl::checkCredit(const Message& msg) { - bool enoughCredit = credit.check(1, qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg)); + boost::intrusive_ptr<const amqp_0_10::MessageTransfer> transfer = protocols.translate(msg); + bool enoughCredit = credit.check(1, transfer->getRequiredCredit()); QPID_LOG(debug, "Subscription " << ConsumerName(*this) << " has " << (enoughCredit ? "sufficient " : "insufficient") - << " credit for message of " << qpid::broker::amqp_0_10::MessageTransfer::getRequiredCredit(msg) << " bytes: " + << " credit for message of " << transfer->getRequiredCredit() << " bytes: " << credit); return enoughCredit; } -SemanticState::ConsumerImpl::~ConsumerImpl() +SemanticStateConsumerImpl::~SemanticStateConsumerImpl() { if (mgmtObject != 0) mgmtObject->resourceDestroy (); @@ -437,9 +446,9 @@ void SemanticState::cancel(ConsumerImpl::shared_ptr c) Queue::shared_ptr queue = c->getQueue(); if(queue) { queue->cancel(c); - if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) { + // Only run auto-delete for counted consumers. + if (c->isCounted() && queue->canAutoDelete() && !queue->hasExclusiveOwner()) Queue::tryAutoDelete(session.getBroker(), queue, connectionId, userID); - } } c->cancel(); } @@ -491,9 +500,8 @@ void SemanticState::requestDispatch() i->second->requestDispatch(); } -void SemanticState::ConsumerImpl::requestDispatch() +void SemanticStateConsumerImpl::requestDispatch() { - assertClusterSafe(); if (blocked) { parent->session.getConnection().outputTasks.addOutputTask(this); parent->session.getConnection().outputTasks.activateOutput(); @@ -510,7 +518,7 @@ bool SemanticState::complete(DeliveryRecord& delivery) return delivery.isRedundant(); } -void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery) +void SemanticStateConsumerImpl::complete(DeliveryRecord& delivery) { if (!delivery.isComplete()) { delivery.complete(); @@ -535,7 +543,7 @@ SessionContext& SemanticState::getSession() { return session; } const SessionContext& SemanticState::getSession() const { return session; } -const SemanticState::ConsumerImpl::shared_ptr SemanticState::find(const std::string& destination) const +const SemanticStateConsumerImpl::shared_ptr SemanticState::find(const std::string& destination) const { ConsumerImpl::shared_ptr consumer; if (!find(destination, consumer)) { @@ -592,37 +600,33 @@ void SemanticState::stop(const std::string& destination) find(destination)->stop(); } -void SemanticState::ConsumerImpl::setWindowMode() +void SemanticStateConsumerImpl::setWindowMode() { - assertClusterSafe(); credit.setWindowMode(true); if (mgmtObject){ mgmtObject->set_creditMode("WINDOW"); } } -void SemanticState::ConsumerImpl::setCreditMode() +void SemanticStateConsumerImpl::setCreditMode() { - assertClusterSafe(); credit.setWindowMode(false); if (mgmtObject){ mgmtObject->set_creditMode("CREDIT"); } } -void SemanticState::ConsumerImpl::addByteCredit(uint32_t value) +void SemanticStateConsumerImpl::addByteCredit(uint32_t value) { - assertClusterSafe(); credit.addByteCredit(value); } -void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value) +void SemanticStateConsumerImpl::addMessageCredit(uint32_t value) { - assertClusterSafe(); credit.addMessageCredit(value); } -bool SemanticState::ConsumerImpl::haveCredit() +bool SemanticStateConsumerImpl::haveCredit() { if (credit) { return true; @@ -632,21 +636,20 @@ bool SemanticState::ConsumerImpl::haveCredit() } } -bool SemanticState::ConsumerImpl::doDispatch() +bool SemanticStateConsumerImpl::doDispatch() { return queue->dispatch(shared_from_this()); } -void SemanticState::ConsumerImpl::flush() +void SemanticStateConsumerImpl::flush() { while(haveCredit() && doDispatch()) ; credit.cancel(); } -void SemanticState::ConsumerImpl::stop() +void SemanticStateConsumerImpl::stop() { - assertClusterSafe(); credit.cancel(); } @@ -700,7 +703,7 @@ void SemanticState::reject(DeliveryId first, DeliveryId last) getSession().setUnackedCount(unacked.size()); } -bool SemanticState::ConsumerImpl::doOutput() +bool SemanticStateConsumerImpl::doOutput() { try { return haveCredit() && doDispatch(); @@ -709,28 +712,26 @@ bool SemanticState::ConsumerImpl::doOutput() } } -void SemanticState::ConsumerImpl::enableNotify() +void SemanticStateConsumerImpl::enableNotify() { Mutex::ScopedLock l(lock); - assertClusterSafe(); notifyEnabled = true; } -void SemanticState::ConsumerImpl::disableNotify() +void SemanticStateConsumerImpl::disableNotify() { Mutex::ScopedLock l(lock); notifyEnabled = false; } -bool SemanticState::ConsumerImpl::isNotifyEnabled() const { +bool SemanticStateConsumerImpl::isNotifyEnabled() const { Mutex::ScopedLock l(lock); return notifyEnabled; } -void SemanticState::ConsumerImpl::notify() +void SemanticStateConsumerImpl::notify() { Mutex::ScopedLock l(lock); - assertClusterSafe(); if (notifyEnabled) { parent->session.getConnection().outputTasks.addOutputTask(this); parent->session.getConnection().outputTasks.activateOutput(); @@ -755,7 +756,6 @@ isInSequenceSetAnd(const SequenceSet& s, Predicate p) { } void SemanticState::accepted(const SequenceSet& commands) { - assertClusterSafe(); if (txBuffer.get()) { //in transactional mode, don't dequeue or remove, just //maintain set of acknowledged messages: @@ -815,4 +815,63 @@ void SemanticState::detached() } } +void SemanticState::addBinding(const string& queueName, const string& exchangeName, + const string& routingKey, const framing::FieldTable& arguments) +{ + QPID_LOG (debug, "SemanticState::addBinding [" + << "queue=" << queueName << ", " + << "exchange=" << exchangeName << ", " + << "key=" << routingKey << ", " + << "args=" << arguments << "]"); + std::string fedOp = arguments.getAsString(qpidFedOp); + if ((arguments.isSet(qpidFedOp)) && (fedOp.empty())) { + fedOp = fedOpBind; + } + std::string fedOrigin = arguments.getAsString(qpidFedOrigin); + if ((arguments.getAsString(X_SCOPE) == SESSION) || (fedOp == fedOpBind)) { + bindings.insert(boost::make_tuple(queueName, exchangeName, routingKey, fedOrigin)); + } + else if (fedOp == fedOpUnbind) { + bindings.erase(boost::make_tuple(queueName, exchangeName, routingKey, fedOrigin)); + } +} + +void SemanticState::removeBinding(const string& queueName, const string& exchangeName, + const string& routingKey) +{ + QPID_LOG (debug, "SemanticState::removeBinding [" + << "queue=" << queueName << ", " + << "exchange=" << exchangeName << ", " + << "key=" << routingKey) + bindings.erase(boost::make_tuple(queueName, exchangeName, routingKey, "")); +} + +void SemanticState::unbindSessionBindings() +{ + //unbind session-scoped bindings + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) { + QPID_LOG (debug, "SemanticState::unbindSessionBindings [" + << "queue=" << i->get<0>() << ", " + << "exchange=" << i->get<1>()<< ", " + << "key=" << i->get<2>() << ", " + << "fedOrigin=" << i->get<3>() << "]"); + try { + std::string fedOrigin = i->get<3>(); + if (!fedOrigin.empty()) { + framing::FieldTable fedArguments; + fedArguments.setString(qpidFedOp, fedOpUnbind); + fedArguments.setString(qpidFedOrigin, fedOrigin); + session.getBroker().bind(i->get<0>(), i->get<1>(), i->get<2>(), fedArguments, + userID, connectionId); + } else { + session.getBroker().unbind(i->get<0>(), i->get<1>(), i->get<2>(), + userID, connectionId); + } + } + catch (...) { + } + } + bindings.clear(); +} + }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h index 9add663e24..ec48ca4753 100644 --- a/cpp/src/qpid/broker/SemanticState.h +++ b/cpp/src/qpid/broker/SemanticState.h @@ -46,10 +46,12 @@ #include <list> #include <map> +#include <set> #include <vector> #include <boost/enable_shared_from_this.hpp> #include <boost/cast.hpp> +#include <boost/tuple/tuple.hpp> namespace qpid { namespace broker { @@ -57,6 +59,7 @@ namespace broker { class Exchange; //class MessageStore; class AsyncStore; +class ProtocolRegistry; class SessionContext; class SessionState; @@ -74,104 +77,18 @@ class SessionState; * called when a client's socket is ready to write data. * */ +class SemanticStateConsumerImpl; class SemanticState : private boost::noncopyable { - public: - class ConsumerImpl : public Consumer, public sys::OutputTask, - public boost::enable_shared_from_this<ConsumerImpl>, - public management::Manageable - { - protected: - mutable qpid::sys::Mutex lock; - SemanticState* const parent; - private: - const boost::shared_ptr<Queue> queue; - const bool ackExpected; - const bool acquire; - bool blocked; - bool exclusive; - std::string resumeId; - const std::string tag; // <destination> from AMQP 0-10 Message.subscribe command - uint64_t resumeTtl; - framing::FieldTable arguments; - Credit credit; - bool notifyEnabled; - const int syncFrequency; - int deliveryCount; - qmf::org::apache::qpid::broker::Subscription* mgmtObject; - - bool checkCredit(const Message& msg); - void allocateCredit(const Message& msg); - bool haveCredit(); - - protected: - QPID_BROKER_EXTERN virtual bool doDispatch(); - size_t unacked() { return parent->unacked.size(); } - QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&, boost::shared_ptr<Consumer>); - - public: - typedef boost::shared_ptr<ConsumerImpl> shared_ptr; - - QPID_BROKER_EXTERN ConsumerImpl(SemanticState* parent, - const std::string& name, boost::shared_ptr<Queue> queue, - bool ack, SubscriptionType type, bool exclusive, - const std::string& tag, const std::string& resumeId, - uint64_t resumeTtl, const framing::FieldTable& arguments); - QPID_BROKER_EXTERN ~ConsumerImpl(); - QPID_BROKER_EXTERN OwnershipToken* getSession(); - QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&); - QPID_BROKER_EXTERN bool filter(const Message&); - QPID_BROKER_EXTERN bool accept(const Message&); - QPID_BROKER_EXTERN void cancel() {} - - QPID_BROKER_EXTERN void disableNotify(); - QPID_BROKER_EXTERN void enableNotify(); - QPID_BROKER_EXTERN void notify(); - QPID_BROKER_EXTERN bool isNotifyEnabled() const; - - QPID_BROKER_EXTERN void requestDispatch(); - - QPID_BROKER_EXTERN void setWindowMode(); - QPID_BROKER_EXTERN void setCreditMode(); - QPID_BROKER_EXTERN void addByteCredit(uint32_t value); - QPID_BROKER_EXTERN void addMessageCredit(uint32_t value); - QPID_BROKER_EXTERN void flush(); - QPID_BROKER_EXTERN void stop(); - QPID_BROKER_EXTERN void complete(DeliveryRecord&); - boost::shared_ptr<Queue> getQueue() const { return queue; } - bool isBlocked() const { return blocked; } - bool setBlocked(bool set) { std::swap(set, blocked); return set; } - - QPID_BROKER_EXTERN bool doOutput(); - - Credit& getCredit() { return credit; } - const Credit& getCredit() const { return credit; } - bool isAckExpected() const { return ackExpected; } - bool isAcquire() const { return acquire; } - bool isExclusive() const { return exclusive; } - std::string getResumeId() const { return resumeId; }; - const std::string& getTag() const { return tag; } - uint64_t getResumeTtl() const { return resumeTtl; } - uint32_t getDeliveryCount() const { return deliveryCount; } - void setDeliveryCount(uint32_t _deliveryCount) { deliveryCount = _deliveryCount; } - const framing::FieldTable& getArguments() const { return arguments; } - - SemanticState& getParent() { return *parent; } - const SemanticState& getParent() const { return *parent; } - - void acknowledged(const DeliveryRecord&) {} - - // manageable entry points - QPID_BROKER_EXTERN management::ManagementObject* - GetManagementObject(void) const; - - QPID_BROKER_EXTERN management::Manageable::status_t - ManagementMethod(uint32_t methodId, management::Args& args, std::string& text); - }; + friend class SemanticStateConsumerImpl; + public: + typedef SemanticStateConsumerImpl ConsumerImpl; typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap; private: - typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap; + typedef std::map<std::string, boost::shared_ptr<ConsumerImpl> > ConsumerImplMap; + typedef boost::tuple<std::string, std::string, std::string, std::string> Binding; + typedef std::set<Binding> Bindings; SessionState& session; ConsumerImplMap consumers; @@ -189,13 +106,16 @@ class SemanticState : private boost::noncopyable { //needed for queue delete events in auto-delete: const std::string connectionId; + Bindings bindings; + void checkDtxTimeout(); bool complete(DeliveryRecord&); AckRange findRange(DeliveryId first, DeliveryId last); void requestDispatch(); - void cancel(ConsumerImpl::shared_ptr); - void disable(ConsumerImpl::shared_ptr); + void cancel(boost::shared_ptr<ConsumerImpl>); + void disable(boost::shared_ptr<ConsumerImpl>); + void unbindSessionBindings(); public: @@ -205,8 +125,8 @@ class SemanticState : private boost::noncopyable { SessionContext& getSession(); const SessionContext& getSession() const; - const ConsumerImpl::shared_ptr find(const std::string& destination) const; - bool find(const std::string& destination, ConsumerImpl::shared_ptr&) const; + const boost::shared_ptr<ConsumerImpl> find(const std::string& destination) const; + bool find(const std::string& destination, boost::shared_ptr<ConsumerImpl>&) const; /** * Get named queue, never returns 0. @@ -257,11 +177,6 @@ class SemanticState : private boost::noncopyable { void detached(); void closed(); - // 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; } @@ -271,6 +186,104 @@ class SemanticState : private boost::noncopyable { void setAccumulatedAck(const framing::SequenceSet& s) { accumulatedAck = s; } void record(const DeliveryRecord& delivery); DtxBufferMap& getSuspendedXids() { return suspendedXids; } + + void addBinding(const std::string& queueName, const std::string& exchangeName, + const std::string& routingKey, const framing::FieldTable& arguments); + void removeBinding(const std::string& queueName, const std::string& exchangeName, + const std::string& routingKey); +}; + +class SemanticStateConsumerImpl : public Consumer, public sys::OutputTask, + public boost::enable_shared_from_this<SemanticStateConsumerImpl>, + public management::Manageable +{ + protected: + mutable qpid::sys::Mutex lock; + SemanticState* const parent; + private: + const boost::shared_ptr<Queue> queue; + const bool ackExpected; + const bool acquire; + bool blocked; + bool exclusive; + std::string resumeId; + const std::string tag; // <destination> from AMQP 0-10 Message.subscribe command + uint64_t resumeTtl; + framing::FieldTable arguments; + Credit credit; + bool notifyEnabled; + const int syncFrequency; + int deliveryCount; + qmf::org::apache::qpid::broker::Subscription::shared_ptr mgmtObject; + ProtocolRegistry& protocols; + + bool checkCredit(const Message& msg); + void allocateCredit(const Message& msg); + bool haveCredit(); + + protected: + QPID_BROKER_EXTERN virtual bool doDispatch(); + size_t unacked() { return parent->unacked.size(); } + QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&, boost::shared_ptr<Consumer>); + + public: + typedef boost::shared_ptr<SemanticStateConsumerImpl> shared_ptr; + + QPID_BROKER_EXTERN SemanticStateConsumerImpl(SemanticState* parent, + const std::string& name, boost::shared_ptr<Queue> queue, + bool ack, SubscriptionType type, bool exclusive, + const std::string& tag, const std::string& resumeId, + uint64_t resumeTtl, const framing::FieldTable& arguments); + QPID_BROKER_EXTERN ~SemanticStateConsumerImpl(); + QPID_BROKER_EXTERN OwnershipToken* getSession(); + QPID_BROKER_EXTERN bool deliver(const QueueCursor&, const Message&); + QPID_BROKER_EXTERN bool filter(const Message&); + QPID_BROKER_EXTERN bool accept(const Message&); + QPID_BROKER_EXTERN void cancel() {} + + QPID_BROKER_EXTERN void disableNotify(); + QPID_BROKER_EXTERN void enableNotify(); + QPID_BROKER_EXTERN void notify(); + QPID_BROKER_EXTERN bool isNotifyEnabled() const; + + QPID_BROKER_EXTERN void requestDispatch(); + + QPID_BROKER_EXTERN void setWindowMode(); + QPID_BROKER_EXTERN void setCreditMode(); + QPID_BROKER_EXTERN void addByteCredit(uint32_t value); + QPID_BROKER_EXTERN void addMessageCredit(uint32_t value); + QPID_BROKER_EXTERN void flush(); + QPID_BROKER_EXTERN void stop(); + QPID_BROKER_EXTERN void complete(DeliveryRecord&); + boost::shared_ptr<Queue> getQueue() const { return queue; } + bool isBlocked() const { return blocked; } + bool setBlocked(bool set) { std::swap(set, blocked); return set; } + + QPID_BROKER_EXTERN bool doOutput(); + + Credit& getCredit() { return credit; } + const Credit& getCredit() const { return credit; } + bool isAckExpected() const { return ackExpected; } + bool isAcquire() const { return acquire; } + bool isExclusive() const { return exclusive; } + std::string getResumeId() const { return resumeId; }; + const std::string& getTag() const { return tag; } + uint64_t getResumeTtl() const { return resumeTtl; } + uint32_t getDeliveryCount() const { return deliveryCount; } + void setDeliveryCount(uint32_t _deliveryCount) { deliveryCount = _deliveryCount; } + const framing::FieldTable& getArguments() const { return arguments; } + + SemanticState& getParent() { return *parent; } + const SemanticState& getParent() const { return *parent; } + + void acknowledged(const DeliveryRecord&) {} + + // manageable entry points + QPID_BROKER_EXTERN management::ManagementObject::shared_ptr + GetManagementObject(void) const; + + QPID_BROKER_EXTERN management::Manageable::status_t + ManagementMethod(uint32_t methodId, management::Args& args, std::string& text); }; }} // namespace qpid::broker diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp index cb2fe15b58..1ea18ea472 100644 --- a/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/cpp/src/qpid/broker/SessionAdapter.cpp @@ -16,7 +16,10 @@ * */ #include "qpid/broker/SessionAdapter.h" + +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" +#include "qpid/broker/DtxTimeout.h" #include "qpid/broker/Queue.h" #include "qpid/Exception.h" #include "qpid/framing/reply_exceptions.h" @@ -98,17 +101,6 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const //exchange already there, not created checkType(response.first, type); checkAlternate(response.first, alternate); - ManagementAgent* agent = getBroker().getManagementAgent(); - if (agent) - agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(), - getConnection().getUserId(), - exchange, - type, - alternateExchange, - durable, - false, - ManagementAgent::toMap(args), - "existing")); QPID_LOG_CAT(debug, model, "Create exchange. name:" << exchange << " user:" << getConnection().getUserId() << " rhost:" << getConnection().getUrl() @@ -165,12 +157,14 @@ void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, { getBroker().bind(queueName, exchangeName, routingKey, arguments, getConnection().getUserId(), getConnection().getUrl()); + state.addBinding(queueName, exchangeName, routingKey, arguments); } void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName, const string& exchangeName, const string& routingKey) { + state.removeBinding(queueName, exchangeName, routingKey); getBroker().unbind(queueName, exchangeName, routingKey, getConnection().getUserId(), getConnection().getUrl()); } @@ -300,6 +294,8 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& } catch (const qpid::types::Exception& e) { throw InvalidArgumentException(e.what()); } + // Identify queues that won't survive a failover. + settings.isTemporary = exclusive && autoDelete && !settings.autoDeleteDelay; std::pair<Queue::shared_ptr, bool> queue_created = getBroker().createQueue(name, settings, @@ -318,11 +314,6 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& 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, alternateExchange, ManagementAgent::toMap(arguments), - "existing")); QPID_LOG_CAT(debug, model, "Create queue. name:" << name << " user:" << getConnection().getUserId() << " rhost:" << getConnection().getUrl() @@ -422,6 +413,11 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, if(!destination.empty() && state.exists(destination)) throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + if (queue->getSettings().isBrowseOnly && acquireMode == 0) { + QPID_LOG(info, "Overriding request to consume from browse-only queue " << queue->getName()); + acquireMode = 1; + } + // We allow browsing (acquireMode == 1) of exclusive queues, this is required by HA. if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session) && acquireMode == 0) throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue " diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp index 9888d12be2..a6b008647f 100644 --- a/cpp/src/qpid/broker/SessionHandler.cpp +++ b/cpp/src/qpid/broker/SessionHandler.cpp @@ -19,8 +19,9 @@ */ #include "qpid/broker/SessionHandler.h" -#include "qpid/broker/SessionState.h" +#include "qpid/broker/Broker.h" #include "qpid/broker/Connection.h" +#include "qpid/broker/SessionState.h" #include "qpid/log/Statement.h" #include <boost/bind.hpp> @@ -34,9 +35,7 @@ using namespace qpid::sys; SessionHandler::SessionHandler(Connection& c, ChannelId ch) : qpid::amqp_0_10::SessionHandler(&c.getOutput(), ch), connection(c), - proxy(out), - clusterOrderProxy(c.getClusterOrderOutput() ? - new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0) + proxy(out) {} SessionHandler::~SessionHandler() {} @@ -110,10 +109,7 @@ void SessionHandler::attachAs(const std::string& name) { SessionId id(connection.getUserId(), name); SessionState::Configuration config = connection.broker.getSessionManager().getSessionConfig(); - // Delay creating management object till attached(). In a cluster, - // only the active link broker calls attachAs but all brokers - // receive the subsequent attached() call. - session.reset(new SessionState(connection.getBroker(), *this, id, config, true)); + session.reset(new SessionState(connection.getBroker(), *this, id, config)); sendAttach(false); } diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h index 21c736fa37..d42b7838bb 100644 --- a/cpp/src/qpid/broker/SessionHandler.h +++ b/cpp/src/qpid/broker/SessionHandler.h @@ -71,17 +71,6 @@ class SessionHandler : public qpid::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(); void attached(const std::string& name);//used by 'pushing' inter-broker bridges void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges @@ -108,7 +97,6 @@ class SessionHandler : public qpid::amqp_0_10::SessionHandler { Connection& connection; framing::AMQP_ClientProxy proxy; std::auto_ptr<SessionState> session; - std::auto_ptr<SetChannelProxy> clusterOrderProxy; boost::shared_ptr<ErrorListener> errorListener; }; diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp index 944cbad0aa..d71134548a 100644 --- a/cpp/src/qpid/broker/SessionState.cpp +++ b/cpp/src/qpid/broker/SessionState.cpp @@ -25,7 +25,6 @@ #include "qpid/broker/DeliveryRecord.h" #include "qpid/broker/SessionManager.h" #include "qpid/broker/SessionHandler.h" -#include "qpid/sys/ClusterSafe.h" #include "qpid/framing/AMQContentBody.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/framing/AMQMethodBody.h" @@ -54,15 +53,14 @@ namespace _qmf = qmf::org::apache::qpid::broker; SessionState::SessionState( Broker& b, SessionHandler& h, const SessionId& id, - const SessionState::Configuration& config, bool delayManagement) + const SessionState::Configuration& config) : qpid::SessionState(id, config), broker(b), handler(&h), semanticState(*this), adapter(semanticState), - mgmtObject(0), asyncCommandCompleter(new AsyncCommandCompleter(this)) { - if (!delayManagement) addManagementObject(); + addManagementObject(); attach(h); } @@ -72,8 +70,8 @@ void SessionState::addManagementObject() { if (parent != 0) { ManagementAgent* agent = getBroker().getManagementAgent(); if (agent != 0) { - mgmtObject = new _qmf::Session - (agent, this, parent, getId().getName()); + mgmtObject = _qmf::Session::shared_ptr(new _qmf::Session + (agent, this, parent, getId().getName())); mgmtObject->set_attached (0); mgmtObject->set_detachedLifespan (0); mgmtObject->clr_expireTime(); @@ -145,14 +143,9 @@ void SessionState::activateOutput() { getConnection().outputTasks.activateOutput(); } -void SessionState::giveReadCredit(int32_t credit) { - if (isAttached()) - getConnection().outputTasks.giveReadCredit(credit); -} - -ManagementObject* SessionState::GetManagementObject (void) const +ManagementObject::shared_ptr SessionState::GetManagementObject(void) const { - return (ManagementObject*) mgmtObject; + return mgmtObject; } Manageable::status_t SessionState::ManagementMethod (uint32_t methodId, @@ -251,11 +244,6 @@ void SessionState::completeRcvMsg(SequenceNumber id, bool requiresAccept, bool requiresSync) { - // Mark this as a cluster-unsafe scope since it can be called in - // journal threads or connection threads as part of asynchronous - // command completion. - sys::ClusterUnsafeScope cus; - bool callSendCompletion = false; receiverCompleted(id); if (requiresAccept) @@ -340,15 +328,9 @@ void SessionState::readyToSend() { Broker& SessionState::getBroker() { return broker; } // Session resume is not fully implemented so it is useless to set a -// non-0 timeout. Moreover it creates problems in a cluster because -// dead sessions are kept and interfere with failover. +// non-0 timeout. void SessionState::setTimeout(uint32_t) { } -framing::AMQP_ClientProxy& SessionState::getClusterOrderProxy() { - return handler->getClusterOrderProxy(); -} - - // Current received command is an execution.sync command. // Complete this command only when all preceding commands have completed. // (called via the invoker() in handleCommand() above) diff --git a/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h index 5e3a77d7ed..a531ec9fc6 100644 --- a/cpp/src/qpid/broker/SessionState.h +++ b/cpp/src/qpid/broker/SessionState.h @@ -41,6 +41,7 @@ #include <boost/scoped_ptr.hpp> #include <boost/intrusive_ptr.hpp> +#include <queue> #include <set> #include <vector> #include <ostream> @@ -73,7 +74,7 @@ class SessionState : public qpid::SessionState, { public: SessionState(Broker&, SessionHandler&, const SessionId&, - const SessionState::Configuration&, bool delayManagement=false); + const SessionState::Configuration&); ~SessionState(); bool isAttached() const { return handler; } @@ -98,7 +99,6 @@ class SessionState : public qpid::SessionState, /** OutputControl **/ void abort(); void activateOutput(); - void giveReadCredit(int32_t); void senderCompleted(const framing::SequenceSet& ranges); @@ -110,17 +110,12 @@ class SessionState : public qpid::SessionState, const qpid::types::Variant::Map& annotations, bool sync); // Manageable entry points - management::ManagementObject* GetManagementObject (void) const; + management::ManagementObject::shared_ptr GetManagementObject(void) const; management::Manageable::status_t 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<qpid::broker::amqp_0_10::MessageTransfer> getMessageInProgress() { return msgBuilder.getMessage(); } - SessionAdapter& getSessionAdapter() { return adapter; } - const SessionId& getSessionId() const { return getId(); } // Used by ExecutionHandler sync command processing. Notifies @@ -153,22 +148,13 @@ class SessionState : public qpid::SessionState, 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; sys::AbsTime expiry; // Used by SessionManager. SemanticState semanticState; SessionAdapter adapter; MessageBuilder msgBuilder; - qmf::org::apache::qpid::broker::Session* mgmtObject; + qmf::org::apache::qpid::broker::Session::shared_ptr mgmtObject; qpid::framing::SequenceSet accepted; // sequence numbers for pending received Execution.Sync commands diff --git a/cpp/src/qpid/broker/StatefulQueueObserver.h b/cpp/src/qpid/broker/StatefulQueueObserver.h deleted file mode 100644 index c682d460b7..0000000000 --- a/cpp/src/qpid/broker/StatefulQueueObserver.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef QPID_BROKER_STATEFULQUEUEOBSERVER_H -#define QPID_BROKER_STATEFULQUEUEOBSERVER_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/QueueObserver.h" -#include "qpid/framing/FieldTable.h" - -namespace qpid { -namespace broker { - -/** - * Specialized type of QueueObserver that maintains internal state that has to - * be replicated across clustered brokers. - */ -class StatefulQueueObserver : public QueueObserver -{ - public: - StatefulQueueObserver(std::string _id) : id(_id) {} - virtual ~StatefulQueueObserver() {} - - /** This identifier must uniquely identify this particular observer amoung - * all observers on a queue. For cluster replication, this id will be used - * to identify the peer queue observer for synchronization across - * brokers. - */ - const std::string& getId() const { return id; } - - /** This method should return the observer's internal state as an opaque - * map. - */ - virtual void getState(qpid::framing::FieldTable& state ) const = 0; - - /** The input map represents the internal state of the peer observer that - * this observer should synchonize to. - */ - virtual void setState(const qpid::framing::FieldTable&) = 0; - - - private: - std::string id; -}; -}} // namespace qpid::broker - -#endif /*!QPID_BROKER_STATEFULQUEUEOBSERVER_H*/ diff --git a/cpp/src/qpid/broker/System.cpp b/cpp/src/qpid/broker/System.cpp index fa8df6406b..8d54427fdc 100644 --- a/cpp/src/qpid/broker/System.cpp +++ b/cpp/src/qpid/broker/System.cpp @@ -31,7 +31,7 @@ using namespace qpid::broker; using namespace std; namespace _qmf = qmf::org::apache::qpid::broker; -System::System (string _dataDir, Broker* broker) : mgmtObject(0) +System::System (string _dataDir, Broker* broker) { ManagementAgent* agent = broker ? broker->getManagementAgent() : 0; @@ -64,7 +64,7 @@ System::System (string _dataDir, Broker* broker) : mgmtObject(0) } } - mgmtObject = new _qmf::System(agent, this, types::Uuid(systemId.c_array())); + mgmtObject = _qmf::System::shared_ptr(new _qmf::System(agent, this, types::Uuid(systemId.c_array()))); qpid::sys::SystemInfo::getSystemId (osName, nodeName, release, diff --git a/cpp/src/qpid/broker/System.h b/cpp/src/qpid/broker/System.h index 6847c662ae..52643fb2d5 100644 --- a/cpp/src/qpid/broker/System.h +++ b/cpp/src/qpid/broker/System.h @@ -35,7 +35,7 @@ class System : public management::Manageable { private: - qmf::org::apache::qpid::broker::System* mgmtObject; + qmf::org::apache::qpid::broker::System::shared_ptr mgmtObject; framing::Uuid systemId; std::string osName, nodeName, release, version, machine; @@ -45,7 +45,7 @@ class System : public management::Manageable System (std::string _dataDir, Broker* broker = 0); - management::ManagementObject* GetManagementObject (void) const + management::ManagementObject::shared_ptr GetManagementObject(void) const { return mgmtObject; } diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp index 38d8f255ac..0badd1621c 100644 --- a/cpp/src/qpid/broker/TopicExchange.cpp +++ b/cpp/src/qpid/broker/TopicExchange.cpp @@ -179,7 +179,7 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons } } - Binding::shared_ptr binding (new Binding (routingPattern, queue, this, FieldTable(), fedOrigin)); + Binding::shared_ptr binding (new Binding (routingPattern, queue, this, args ? *args : FieldTable(), fedOrigin)); binding->startManagement(); bk->bindingVector.push_back(binding); nBindings++; diff --git a/cpp/src/qpid/broker/TxAccept.h b/cpp/src/qpid/broker/TxAccept.h index a59e69a85f..daf192285a 100644 --- a/cpp/src/qpid/broker/TxAccept.h +++ b/cpp/src/qpid/broker/TxAccept.h @@ -71,9 +71,6 @@ namespace qpid { virtual void commit() throw(); virtual void rollback() throw(); virtual ~TxAccept(){} - - // Used by cluster replication. - const framing::SequenceSet& getAcked() const { return acked; } }; } } diff --git a/cpp/src/qpid/broker/TxOpVisitor.h b/cpp/src/qpid/broker/TxOpVisitor.h deleted file mode 100644 index e69de29bb2..0000000000 --- a/cpp/src/qpid/broker/TxOpVisitor.h +++ /dev/null diff --git a/cpp/src/qpid/broker/TxPublish.cpp b/cpp/src/qpid/broker/TxPublish.cpp deleted file mode 100644 index e69de29bb2..0000000000 --- a/cpp/src/qpid/broker/TxPublish.cpp +++ /dev/null diff --git a/cpp/src/qpid/broker/TxPublish.h b/cpp/src/qpid/broker/TxPublish.h deleted file mode 100644 index e69de29bb2..0000000000 --- a/cpp/src/qpid/broker/TxPublish.h +++ /dev/null diff --git a/cpp/src/qpid/broker/Vhost.cpp b/cpp/src/qpid/broker/Vhost.cpp index a9ca3b42ab..e72118b570 100644 --- a/cpp/src/qpid/broker/Vhost.cpp +++ b/cpp/src/qpid/broker/Vhost.cpp @@ -29,7 +29,7 @@ namespace qpid { namespace management { class Manageable; }} -Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmtObject(0) +Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) { if (parentBroker != 0 && broker != 0) { @@ -37,7 +37,7 @@ Vhost::Vhost (qpid::management::Manageable* parentBroker, Broker* broker) : mgmt if (agent != 0) { - mgmtObject = new _qmf::Vhost(agent, this, parentBroker, "/"); + mgmtObject = _qmf::Vhost::shared_ptr(new _qmf::Vhost(agent, this, parentBroker, "/")); agent->addObject(mgmtObject, 0, true); } } diff --git a/cpp/src/qpid/broker/Vhost.h b/cpp/src/qpid/broker/Vhost.h index 9554d641c2..599b821870 100644 --- a/cpp/src/qpid/broker/Vhost.h +++ b/cpp/src/qpid/broker/Vhost.h @@ -32,7 +32,7 @@ class Vhost : public management::Manageable { private: - qmf::org::apache::qpid::broker::Vhost* mgmtObject; + qmf::org::apache::qpid::broker::Vhost::shared_ptr mgmtObject; public: @@ -40,7 +40,7 @@ class Vhost : public management::Manageable Vhost (management::Manageable* parentBroker, Broker* broker = 0); - management::ManagementObject* GetManagementObject (void) const + management::ManagementObject::shared_ptr GetManagementObject (void) const { return mgmtObject; } void setFederationTag(const std::string& tag); }; diff --git a/cpp/src/qpid/broker/amqp/Connection.cpp b/cpp/src/qpid/broker/amqp/Connection.cpp new file mode 100644 index 0000000000..1f135cf931 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Connection.cpp @@ -0,0 +1,247 @@ +/* + * + * 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 "Connection.h" +#include "Session.h" +#include "qpid/Exception.h" +#include "qpid/broker/Broker.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/OutputControl.h" +#include <sstream> +extern "C" { +#include <proton/engine.h> +#include <proton/error.h> +} + +namespace qpid { +namespace broker { +namespace amqp { + +Connection::Connection(qpid::sys::OutputControl& o, const std::string& i, qpid::broker::Broker& b, bool saslInUse) + : ManagedConnection(b, i), + connection(pn_connection()), + transport(pn_transport()), + out(o), id(i), broker(b), haveOutput(true) +{ + if (pn_transport_bind(transport, connection)) { + //error + } + out.activateOutput(); + bool enableTrace(false); + QPID_LOG_TEST_CAT(trace, protocol, enableTrace); + if (enableTrace) pn_transport_trace(transport, PN_TRACE_FRM); + + if (!saslInUse) { + //feed in a dummy AMQP 1.0 header as engine expects one, but + //we already read it (if sasl is in use we read the sasl + //header,not the AMQP 1.0 header). + std::vector<char> protocolHeader(8); + qpid::framing::ProtocolInitiation pi(getVersion()); + qpid::framing::Buffer buffer(&protocolHeader[0], protocolHeader.size()); + pi.encode(buffer); + pn_transport_input(transport, &protocolHeader[0], protocolHeader.size()); + + //wont get a userid, so set a dummy one on the ManagedConnection to trigger event + setUserid("no authentication used"); + } +} + + +Connection::~Connection() +{ + + pn_transport_free(transport); + pn_connection_free(connection); +} + +pn_transport_t* Connection::getTransport() +{ + return transport; +} +size_t Connection::decode(const char* buffer, size_t size) +{ + QPID_LOG(trace, id << " decode(" << size << ")") + //TODO: Fix pn_engine_input() to take const buffer + ssize_t n = pn_transport_input(transport, const_cast<char*>(buffer), size); + if (n > 0 || n == PN_EOS) { + //If engine returns EOS, have no way of knowing how many bytes + //it processed, but can assume none need to be reprocessed so + //consider them all read: + if (n == PN_EOS) n = size; + QPID_LOG_CAT(debug, network, id << " decoded " << n << " bytes from " << size) + process(); + pn_transport_tick(transport, 0); + if (!haveOutput) { + haveOutput = true; + out.activateOutput(); + } + return n; + } else if (n == PN_ERR) { + throw qpid::Exception(QPID_MSG("Error on input: " << getError())); + } else { + return 0; + } +} + +size_t Connection::encode(char* buffer, size_t size) +{ + QPID_LOG(trace, "encode(" << size << ")") + ssize_t n = pn_transport_output(transport, buffer, size); + if (n > 0) { + QPID_LOG_CAT(debug, network, id << " encoded " << n << " bytes from " << size) + haveOutput = true; + return n; + } else if (n == PN_EOS) { + haveOutput = size; + return size;//Is this right? + } else if (n == PN_ERR) { + throw qpid::Exception(QPID_MSG("Error on output: " << getError())); + } else { + haveOutput = false; + return 0; + } +} +bool Connection::canEncode() +{ + for (Sessions::iterator i = sessions.begin();i != sessions.end(); ++i) { + if (i->second->dispatch()) haveOutput = true; + } + process(); + //TODO: proper handling of time in and out of tick + pn_transport_tick(transport, 0); + QPID_LOG_CAT(trace, network, id << " canEncode(): " << haveOutput) + return haveOutput; +} +void Connection::closed() +{ + //TODO: tear down sessions and associated links + for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) { + i->second->close(); + } +} +bool Connection::isClosed() const +{ + return pn_connection_state(connection) & PN_REMOTE_CLOSED; +} +framing::ProtocolVersion Connection::getVersion() const +{ + return qpid::framing::ProtocolVersion(1,0); +} +namespace { +pn_state_t REQUIRES_OPEN = PN_LOCAL_UNINIT | PN_REMOTE_ACTIVE; +pn_state_t REQUIRES_CLOSE = PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED; +} + +void Connection::process() +{ + QPID_LOG(trace, id << " process()"); + if ((pn_connection_state(connection) & REQUIRES_OPEN) == REQUIRES_OPEN) { + QPID_LOG_CAT(debug, model, id << " connection opened"); + pn_connection_set_container(connection, broker.getFederationTag().c_str()); + pn_connection_open(connection); + } + + for (pn_session_t* s = pn_session_head(connection, REQUIRES_OPEN); s; s = pn_session_next(s, REQUIRES_OPEN)) { + QPID_LOG_CAT(debug, model, id << " session begun"); + pn_session_open(s); + boost::shared_ptr<Session> ssn(new Session(s, broker, *this, out)); + sessions[s] = ssn; + } + for (pn_link_t* l = pn_link_head(connection, REQUIRES_OPEN); l; l = pn_link_next(l, REQUIRES_OPEN)) { + pn_link_open(l); + + Sessions::iterator session = sessions.find(pn_link_session(l)); + if (session == sessions.end()) { + QPID_LOG(error, id << " Link attached on unknown session!"); + } else { + try { + session->second->attach(l); + QPID_LOG_CAT(debug, protocol, id << " link " << l << " attached on " << pn_link_session(l)); + } catch (const std::exception& e) { + QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); + //TODO: set error details on detach when that is exposed via engine API + pn_link_close(l); + } + } + } + + //handle deliveries + for (pn_delivery_t* delivery = pn_work_head(connection); delivery; delivery = pn_work_next(delivery)) { + pn_link_t* link = pn_delivery_link(delivery); + if (pn_link_is_receiver(link)) { + Sessions::iterator i = sessions.find(pn_link_session(link)); + if (i != sessions.end()) { + i->second->incoming(link, delivery); + } else { + pn_delivery_update(delivery, PN_REJECTED); + } + } else { //i.e. SENDER + Sessions::iterator i = sessions.find(pn_link_session(link)); + if (i != sessions.end()) { + QPID_LOG(trace, id << " handling outgoing delivery for " << link << " on session " << pn_link_session(link)); + i->second->outgoing(link, delivery); + } else { + QPID_LOG(error, id << " Got delivery for non-existent session: " << pn_link_session(link) << ", link: " << link); + } + } + } + + + for (pn_link_t* l = pn_link_head(connection, REQUIRES_CLOSE); l; l = pn_link_next(l, REQUIRES_CLOSE)) { + pn_link_close(l); + Sessions::iterator session = sessions.find(pn_link_session(l)); + if (session == sessions.end()) { + QPID_LOG(error, id << " peer attempted to detach link on unknown session!"); + } else { + session->second->detach(l); + QPID_LOG_CAT(debug, model, id << " link detached"); + } + } + for (pn_session_t* s = pn_session_head(connection, REQUIRES_CLOSE); s; s = pn_session_next(s, REQUIRES_CLOSE)) { + pn_session_close(s); + Sessions::iterator i = sessions.find(s); + if (i != sessions.end()) { + i->second->close(); + sessions.erase(i); + QPID_LOG_CAT(debug, model, id << " session ended"); + } else { + QPID_LOG(error, id << " peer attempted to close unrecognised session"); + } + } + if ((pn_connection_state(connection) & REQUIRES_CLOSE) == REQUIRES_CLOSE) { + QPID_LOG_CAT(debug, model, id << " connection closed"); + pn_connection_close(connection); + } +} + +std::string Connection::getError() +{ + std::stringstream text; + pn_error_t* cerror = pn_connection_error(connection); + if (cerror) text << "connection error " << pn_error_text(cerror); + pn_error_t* terror = pn_transport_error(transport); + if (terror) text << "transport error " << pn_error_text(terror); + return text.str(); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Connection.h b/cpp/src/qpid/broker/amqp/Connection.h new file mode 100644 index 0000000000..8af209af7a --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Connection.h @@ -0,0 +1,73 @@ +#ifndef QPID_BROKER_AMQP1_CONNECTION_H +#define QPID_BROKER_AMQP1_CONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/broker/amqp/ManagedConnection.h" +#include <map> +#include <boost/shared_ptr.hpp> + +struct pn_connection_t; +struct pn_session_t; +struct pn_transport_t; + +namespace qpid { +namespace broker { + +class Broker; + +namespace amqp { + +class Session; +/** + * AMQP 1.0 protocol support for broker + */ +class Connection : public sys::ConnectionCodec, public ManagedConnection +{ + public: + Connection(qpid::sys::OutputControl& out, const std::string& id, qpid::broker::Broker& broker, bool saslInUse); + ~Connection(); + size_t decode(const char* buffer, size_t size); + size_t encode(char* buffer, size_t size); + bool canEncode(); + + void closed(); + bool isClosed() const; + + framing::ProtocolVersion getVersion() const; + pn_transport_t* getTransport(); + private: + typedef std::map<pn_session_t*, boost::shared_ptr<Session> > Sessions; + pn_connection_t* connection; + pn_transport_t* transport; + qpid::sys::OutputControl& out; + const std::string id; + qpid::broker::Broker& broker; + bool haveOutput; + Sessions sessions; + + void process(); + std::string getError(); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_CONNECTION_H*/ diff --git a/cpp/src/qpid/broker/amqp/DataReader.cpp b/cpp/src/qpid/broker/amqp/DataReader.cpp new file mode 100644 index 0000000000..519dd71c9c --- /dev/null +++ b/cpp/src/qpid/broker/amqp/DataReader.cpp @@ -0,0 +1,187 @@ +/* + * + * 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 "DataReader.h" +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/log/Statement.h" +#include <string> +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace broker { +namespace amqp { +namespace { +qpid::amqp::CharSequence convert(pn_bytes_t in) +{ + qpid::amqp::CharSequence out; + out.data = in.start; + out.size = in.size; + return out; +} + +qpid::amqp::CharSequence convert(pn_uuid_t in) +{ + qpid::amqp::CharSequence out; + out.data = in.bytes; + out.size = 16; + return out; +} +} + +DataReader::DataReader(qpid::amqp::Reader& r) : reader(r) {} + +void DataReader::read(pn_data_t* data) +{ + /* + while (pn_data_next(data)) { + readOne(data); + } + */ + do { + readOne(data); + } while (pn_data_next(data)); +} +void DataReader::readOne(pn_data_t* data) +{ + qpid::amqp::Descriptor descriptor(0); + bool described = pn_data_is_described(data); + if (described) { + pn_data_enter(data); + pn_data_next(data); + if (pn_data_type(data) == PN_ULONG) { + descriptor = qpid::amqp::Descriptor(pn_data_get_ulong(data)); + } else if (pn_data_type(data) == PN_SYMBOL) { + descriptor = qpid::amqp::Descriptor(convert(pn_data_get_symbol(data))); + } else { + QPID_LOG(notice, "Ignoring descriptor of type " << pn_data_type(data)); + } + pn_data_next(data); + } + switch (pn_data_type(data)) { + case PN_NULL: + reader.onNull(described ? &descriptor : 0); + break; + case PN_BOOL: + reader.onBoolean(pn_data_get_bool(data), described ? &descriptor : 0); + break; + case PN_UBYTE: + reader.onUByte(pn_data_get_ubyte(data), described ? &descriptor : 0); + break; + case PN_BYTE: + reader.onByte(pn_data_get_byte(data), described ? &descriptor : 0); + break; + case PN_USHORT: + reader.onUShort(pn_data_get_ushort(data), described ? &descriptor : 0); + break; + case PN_SHORT: + reader.onShort(pn_data_get_short(data), described ? &descriptor : 0); + break; + case PN_UINT: + reader.onUInt(pn_data_get_uint(data), described ? &descriptor : 0); + break; + case PN_INT: + reader.onInt(pn_data_get_int(data), described ? &descriptor : 0); + break; + case PN_CHAR: + pn_data_get_char(data); + break; + case PN_ULONG: + reader.onULong(pn_data_get_ulong(data), described ? &descriptor : 0); + break; + case PN_LONG: + reader.onLong(pn_data_get_long(data), described ? &descriptor : 0); + break; + case PN_TIMESTAMP: + reader.onTimestamp(pn_data_get_timestamp(data), described ? &descriptor : 0); + break; + case PN_FLOAT: + reader.onFloat(pn_data_get_float(data), described ? &descriptor : 0); + break; + case PN_DOUBLE: + reader.onDouble(pn_data_get_double(data), described ? &descriptor : 0); + break; + case PN_DECIMAL32: + pn_data_get_decimal32(data); + break; + case PN_DECIMAL64: + pn_data_get_decimal64(data); + break; + case PN_DECIMAL128: + pn_data_get_decimal128(data); + break; + case PN_UUID: + reader.onUuid(convert(pn_data_get_uuid(data)), described ? &descriptor : 0); + break; + case PN_BINARY: + reader.onBinary(convert(pn_data_get_binary(data)), described ? &descriptor : 0); + break; + case PN_STRING: + reader.onString(convert(pn_data_get_string(data)), described ? &descriptor : 0); + break; + case PN_SYMBOL: + reader.onSymbol(convert(pn_data_get_symbol(data)), described ? &descriptor : 0); + break; + case PN_DESCRIBED: + break; + case PN_ARRAY: + readArray(data, described ? &descriptor : 0); + break; + case PN_LIST: + readList(data, described ? &descriptor : 0); + break; + case PN_MAP: + readMap(data, described ? &descriptor : 0); + break; + } + if (described) pn_data_exit(data); +} + +void DataReader::readArray(pn_data_t* /*data*/, const qpid::amqp::Descriptor* /*descriptor*/) +{ + //not yet implemented +} + +void DataReader::readList(pn_data_t* data, const qpid::amqp::Descriptor* descriptor) +{ + size_t count = pn_data_get_list(data); + reader.onStartList(count, qpid::amqp::CharSequence(), descriptor); + pn_data_enter(data); + for (size_t i = 0; i < count && pn_data_next(data); ++i) { + read(data); + } + pn_data_exit(data); + reader.onEndList(count, descriptor); +} + +void DataReader::readMap(pn_data_t* data, const qpid::amqp::Descriptor* descriptor) +{ + size_t count = pn_data_get_map(data); + reader.onStartMap(count, qpid::amqp::CharSequence(), descriptor); + pn_data_enter(data); + for (size_t i = 0; i < count && pn_data_next(data); ++i) { + read(data); + } + pn_data_exit(data); + reader.onEndMap(count, descriptor); +} +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/DataReader.h b/cpp/src/qpid/broker/amqp/DataReader.h new file mode 100644 index 0000000000..024507e7f2 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/DataReader.h @@ -0,0 +1,53 @@ +#ifndef QPID_BROKER_AMQP_DATAREADER_H +#define QPID_BROKER_AMQP_DATAREADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Reader.h" + +struct pn_data_t; + +namespace qpid { +namespace amqp { +struct Descriptor; +} +namespace broker { +namespace amqp { + +/** + * Allows use of Reader interface to read pn_data_t* data. + */ +class DataReader +{ + public: + DataReader(qpid::amqp::Reader& reader); + void read(pn_data_t*); + private: + qpid::amqp::Reader& reader; + + void readOne(pn_data_t*); + void readMap(pn_data_t*, const qpid::amqp::Descriptor*); + void readList(pn_data_t*, const qpid::amqp::Descriptor*); + void readArray(pn_data_t*, const qpid::amqp::Descriptor*); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_DATAREADER_H*/ diff --git a/cpp/src/qpid/broker/amqp/Filter.cpp b/cpp/src/qpid/broker/amqp/Filter.cpp new file mode 100644 index 0000000000..38baba0df1 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Filter.cpp @@ -0,0 +1,150 @@ +/* + * + * 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/amqp/Filter.h" +#include "qpid/broker/amqp/DataReader.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/log/Statement.h" +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace broker { +namespace amqp { + +void Filter::read(pn_data_t* data) +{ + try { + DataReader reader(*this); + reader.read(data); + } catch (const std::exception& e) { + QPID_LOG(warning, "Error parsing filter: " << e.what()); + } +} + +void Filter::write(pn_data_t* data) +{ + pn_data_put_map(data); + pn_data_enter(data); + subjectFilter.write(data); + pn_data_exit(data); +} + +void Filter::onStringValue(const qpid::amqp::CharSequence& key, const qpid::amqp::CharSequence& value, const qpid::amqp::Descriptor* descriptor) +{ + StringFilter filter; + filter.key = std::string(key.data, key.size); + filter.value = std::string(value.data, value.size); + if (descriptor) { + filter.described = true; + filter.descriptor = *descriptor; + if (descriptor->match(qpid::amqp::filters::LEGACY_TOPIC_FILTER_SYMBOL, qpid::amqp::filters::LEGACY_TOPIC_FILTER_CODE) + || descriptor->match(qpid::amqp::filters::LEGACY_DIRECT_FILTER_SYMBOL, qpid::amqp::filters::LEGACY_DIRECT_FILTER_CODE)) { + setSubjectFilter(filter); + } else { + QPID_LOG(notice, "Skipping unrecognised string filter with key " << filter.key << " and descriptor " << filter.descriptor); + } + } else { + setSubjectFilter(filter); + } +} + +bool Filter::hasSubjectFilter() const +{ + return !subjectFilter.value.empty(); +} + +std::string Filter::getSubjectFilter() const +{ + return subjectFilter.value; +} + + +void Filter::setSubjectFilter(const StringFilter& filter) +{ + if (hasSubjectFilter()) { + QPID_LOG(notice, "Skipping filter with key " << filter.key << "; subject filter already set"); + } else { + subjectFilter = filter; + } +} + +void Filter::bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue) +{ + subjectFilter.bind(exchange, queue); +} + +Filter::StringFilter::StringFilter() : described(false), descriptor(0) {} +namespace { +pn_bytes_t convert(const std::string& in) +{ + pn_bytes_t out; + out.start = const_cast<char*>(in.data()); + out.size = in.size(); + return out; +} +pn_bytes_t convert(const qpid::amqp::CharSequence& in) +{ + pn_bytes_t out; + out.start = const_cast<char*>(in.data); + out.size = in.size; + return out; +} +} +void Filter::StringFilter::write(pn_data_t* data) +{ + pn_data_put_symbol(data, convert(key)); + if (described) { + pn_data_put_described(data); + pn_data_enter(data); + switch (descriptor.type) { + case qpid::amqp::Descriptor::NUMERIC: + pn_data_put_ulong(data, descriptor.value.code); + break; + case qpid::amqp::Descriptor::SYMBOLIC: + pn_data_put_symbol(data, convert(descriptor.value.symbol)); + break; + } + } + pn_data_put_string(data, convert(value)); + if (described) pn_data_exit(data); +} + +void Filter::StringFilter::bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue) +{ + if (described && exchange->getType() == DirectExchange::typeName + && descriptor.match(qpid::amqp::filters::LEGACY_TOPIC_FILTER_SYMBOL, qpid::amqp::filters::LEGACY_TOPIC_FILTER_CODE)) { + QPID_LOG(info, "Using legacy topic filter as a direct matching filter for " << exchange->getName()); + if (descriptor.type == qpid::amqp::Descriptor::NUMERIC) { + descriptor = qpid::amqp::Descriptor(qpid::amqp::filters::LEGACY_DIRECT_FILTER_CODE); + } else { + qpid::amqp::CharSequence symbol; + symbol.data = qpid::amqp::filters::LEGACY_DIRECT_FILTER_SYMBOL.data(); + symbol.size = qpid::amqp::filters::LEGACY_DIRECT_FILTER_SYMBOL.size(); + descriptor = qpid::amqp::Descriptor(symbol); + } + } + exchange->bind(queue, value, 0); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Filter.h b/cpp/src/qpid/broker/amqp/Filter.h new file mode 100644 index 0000000000..20cceb372a --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Filter.h @@ -0,0 +1,63 @@ +#ifndef QPID_BROKER_AMQP_FILTER_H +#define QPID_BROKER_AMQP_FILTER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MapReader.h" +#include "qpid/amqp/Descriptor.h" +#include <boost/shared_ptr.hpp> + +struct pn_data_t; +namespace qpid { +namespace broker { +class Exchange; +class Queue; +namespace amqp { + + +class Filter : qpid::amqp::MapReader +{ + public: + void read(pn_data_t*); + void write(pn_data_t*); + bool hasSubjectFilter() const; + std::string getSubjectFilter() const; + void bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue); + private: + struct StringFilter + { + bool described; + qpid::amqp::Descriptor descriptor; + std::string key; + std::string value; + StringFilter(); + void write(pn_data_t*); + void bind(boost::shared_ptr<Exchange> exchange, boost::shared_ptr<Queue> queue); + }; + + void onStringValue(const qpid::amqp::CharSequence& key, const qpid::amqp::CharSequence& value, const qpid::amqp::Descriptor* descriptor); + void setSubjectFilter(const StringFilter&); + + StringFilter subjectFilter; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_FILTER_H*/ diff --git a/cpp/src/qpid/broker/amqp/Header.cpp b/cpp/src/qpid/broker/amqp/Header.cpp new file mode 100644 index 0000000000..493e757a56 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Header.cpp @@ -0,0 +1,65 @@ +/* + * + * 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/amqp/Header.h" +#include "qpid/broker/Message.h" + +namespace qpid { +namespace broker { +namespace amqp { + +bool Header::isDurable() const +{ + return message.isPersistent(); +} + +uint8_t Header::getPriority() const +{ + return message.getPriority(); +} + +bool Header::hasTtl() const +{ + uint64_t dummy(0); + return message.getTtl(dummy); +} + +uint32_t Header::getTtl() const +{ + uint64_t ttl(0); + message.getTtl(ttl); + if (ttl > std::numeric_limits<uint32_t>::max()) return std::numeric_limits<uint32_t>::max(); + else return (uint32_t) ttl; +} + +bool Header::isFirstAcquirer() const +{ + return false;//TODO +} + +uint32_t Header::getDeliveryCount() const +{ + return message.getDeliveryCount(); +} + +Header::Header(const qpid::broker::Message& m) : message(m) {} + + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Header.h b/cpp/src/qpid/broker/amqp/Header.h new file mode 100644 index 0000000000..6e4f763028 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Header.h @@ -0,0 +1,50 @@ +#ifndef QPID_BROKER_AMQP_HEADER_H +#define QPID_BROKER_AMQP_HEADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MessageEncoder.h" + +namespace qpid { +namespace broker { +class Message; +namespace amqp { + +/** + * Adapts the broker current message abstraction to provide that + * required by the AMQP 1.0 message encoder. + */ +class Header : public qpid::amqp::MessageEncoder::Header +{ + public: + Header(const qpid::broker::Message&); + bool isDurable() const; + uint8_t getPriority() const; + bool hasTtl() const; + uint32_t getTtl() const; + bool isFirstAcquirer() const; + uint32_t getDeliveryCount() const; + private: + const qpid::broker::Message& message; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_HEADER_H*/ diff --git a/cpp/src/qpid/broker/amqp/ManagedConnection.cpp b/cpp/src/qpid/broker/amqp/ManagedConnection.cpp new file mode 100644 index 0000000000..0253ba5552 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedConnection.cpp @@ -0,0 +1,98 @@ +/* + * + * 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/amqp/ManagedConnection.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" +#include "qmf/org/apache/qpid/broker/EventClientConnect.h" +#include "qmf/org/apache/qpid/broker/EventClientDisconnect.h" + +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { +namespace amqp { + +ManagedConnection::ManagedConnection(Broker& broker, const std::string i) : id(i), agent(0) +{ + //management integration: + agent = broker.getManagementAgent(); + if (agent != 0) { + qpid::management::Manageable* parent = broker.GetVhostObject(); + // TODO set last bool true if system connection + connection = _qmf::Connection::shared_ptr(new _qmf::Connection(agent, this, parent, id, true, false, "AMQP 1.0")); + connection->set_shadow(false); + agent->addObject(connection); + } +} + +ManagedConnection::~ManagedConnection() +{ + if (agent && connection) { + agent->raiseEvent(_qmf::EventClientDisconnect(id, userid, connection->get_remoteProperties())); + connection->resourceDestroy(); + } + QPID_LOG_CAT(debug, model, "Delete connection. user:" << userid << " rhost:" << id); +} + +void ManagedConnection::setUserid(const std::string& uid) +{ + userid = uid; + if (agent && connection) { + connection->set_authIdentity(userid); + agent->raiseEvent(_qmf::EventClientConnect(id, userid, connection->get_remoteProperties())); + } + QPID_LOG_CAT(debug, model, "Create connection. user:" << userid << " rhost:" << id ); +} + +void ManagedConnection::setSaslMechanism(const std::string& mechanism) +{ + connection->set_saslMechanism(mechanism); +} + +void ManagedConnection::setSaslSsf(int ssf) +{ + connection->set_saslSsf(ssf); +} + +qpid::management::ManagementObject::shared_ptr ManagedConnection::GetManagementObject() const +{ + return connection; +} + +std::string ManagedConnection::getId() const { return id; } +std::string ManagedConnection::getUserid() const { return userid; } + +bool ManagedConnection::isLocal(const ConnectionToken* t) const +{ + return this == t; +} +void ManagedConnection::outgoingMessageSent() +{ + if (connection) connection->inc_msgsToClient(); +} + +void ManagedConnection::incomingMessageReceived() +{ + if (connection) connection->inc_msgsFromClient(); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/ManagedConnection.h b/cpp/src/qpid/broker/amqp/ManagedConnection.h new file mode 100644 index 0000000000..e2d0376918 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedConnection.h @@ -0,0 +1,59 @@ +#ifndef QPID_BROKER_AMQP_MANAGEDCONNECTION_H +#define QPID_BROKER_AMQP_MANAGEDCONNECTION_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/management/Manageable.h" +#include "qpid/broker/ConnectionToken.h" +#include "qmf/org/apache/qpid/broker/Connection.h" + +namespace qpid { +namespace management { +class ManagementAgent; +class ManagementObject; +} +namespace broker { +class Broker; +namespace amqp { + +class ManagedConnection : public qpid::management::Manageable, public ConnectionToken +{ + public: + ManagedConnection(Broker& broker, const std::string id); + virtual ~ManagedConnection(); + void setUserid(const std::string&); + std::string getId() const; + std::string getUserid() const; + void setSaslMechanism(const std::string&); + void setSaslSsf(int); + qpid::management::ManagementObject::shared_ptr GetManagementObject() const; + bool isLocal(const ConnectionToken* t) const; + void incomingMessageReceived(); + void outgoingMessageSent(); + private: + const std::string id; + std::string userid; + qmf::org::apache::qpid::broker::Connection::shared_ptr connection; + qpid::management::ManagementAgent* agent; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDCONNECTION_H*/ diff --git a/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp new file mode 100644 index 0000000000..f36a1e8da4 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp @@ -0,0 +1,70 @@ +/* + * + * 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 "ManagedOutgoingLink.h" +#include "qpid/broker/amqp/ManagedSession.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Queue.h" +#include "qpid/types/Variant.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" + +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { +namespace amqp { + +ManagedOutgoingLink::ManagedOutgoingLink(Broker& broker, Queue& q, ManagedSession& p, const std::string i, bool topic) + : parent(p), id(i) +{ + qpid::management::ManagementAgent* agent = broker.getManagementAgent(); + if (agent) { + subscription = _qmf::Subscription::shared_ptr(new _qmf::Subscription(agent, this, &p, q.GetManagementObject()->getObjectId(), id, + false/*FIXME*/, true/*FIXME*/, topic, qpid::types::Variant::Map())); + agent->addObject(subscription); + subscription->set_creditMode("n/a"); + } +} +ManagedOutgoingLink::~ManagedOutgoingLink() +{ + if (subscription != 0) subscription->resourceDestroy(); +} + +qpid::management::ManagementObject::shared_ptr ManagedOutgoingLink::GetManagementObject() const +{ + return subscription; +} + +void ManagedOutgoingLink::outgoingMessageSent() +{ + if (subscription) { subscription->inc_delivered(); } + parent.outgoingMessageSent(); +} +void ManagedOutgoingLink::outgoingMessageAccepted() +{ + parent.outgoingMessageAccepted(); +} +void ManagedOutgoingLink::outgoingMessageRejected() +{ + parent.outgoingMessageRejected(); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h new file mode 100644 index 0000000000..20a1095db2 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h @@ -0,0 +1,53 @@ +#ifndef QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H +#define QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_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/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Subscription.h" + +namespace qpid { +namespace management { +class ManagementObject; +} +namespace broker { +class Broker; +class Queue; +namespace amqp { +class ManagedSession; + +class ManagedOutgoingLink : public qpid::management::Manageable +{ + public: + ManagedOutgoingLink(Broker& broker, Queue&, ManagedSession& parent, const std::string id, bool topic); + virtual ~ManagedOutgoingLink(); + qpid::management::ManagementObject::shared_ptr GetManagementObject() const; + void outgoingMessageSent(); + void outgoingMessageAccepted(); + void outgoingMessageRejected(); + private: + ManagedSession& parent; + const std::string id; + qmf::org::apache::qpid::broker::Subscription::shared_ptr subscription; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H*/ diff --git a/cpp/src/qpid/broker/amqp/ManagedSession.cpp b/cpp/src/qpid/broker/amqp/ManagedSession.cpp new file mode 100644 index 0000000000..9bef0e842b --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedSession.cpp @@ -0,0 +1,88 @@ +/* + * + * 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/amqp/ManagedSession.h" +#include "qpid/broker/amqp/ManagedConnection.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" + +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { +namespace amqp { + +ManagedSession::ManagedSession(Broker& broker, ManagedConnection& p, const std::string i) : parent(p), id(i), unacked(0) +{ + qpid::management::ManagementAgent* agent = broker.getManagementAgent(); + if (agent != 0) { + session = _qmf::Session::shared_ptr(new _qmf::Session(agent, this, broker.GetVhostObject(), id)); + session->set_attached(true); + session->set_detachedLifespan(0); + session->clr_expireTime(); + session->set_connectionRef(parent.GetManagementObject()->getObjectId()); + agent->addObject(session); + } +} + +ManagedSession::~ManagedSession() +{ + if (session) session->resourceDestroy(); +} + +qpid::management::ManagementObject::shared_ptr ManagedSession::GetManagementObject() const +{ + return session; +} + +bool ManagedSession::isLocal(const ConnectionToken* t) const +{ + return &parent == t; +} + +void ManagedSession::outgoingMessageSent() +{ + if (session) session->set_unackedMessages(++unacked); + parent.outgoingMessageSent(); +} +void ManagedSession::outgoingMessageAccepted() +{ + if (session) session->set_unackedMessages(--unacked); +} +void ManagedSession::outgoingMessageRejected() +{ + if (session) session->set_unackedMessages(--unacked); +} + +void ManagedSession::incomingMessageReceived() +{ + parent.incomingMessageReceived(); +} +void ManagedSession::incomingMessageAccepted() +{ + +} +void ManagedSession::incomingMessageRejected() +{ + +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/ManagedSession.h b/cpp/src/qpid/broker/amqp/ManagedSession.h new file mode 100644 index 0000000000..1f56964bb6 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedSession.h @@ -0,0 +1,59 @@ +#ifndef QPID_BROKER_AMQP_MANAGEDSESSION_H +#define QPID_BROKER_AMQP_MANAGEDSESSION_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/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Session.h" +#include "qpid/broker/ConnectionToken.h" +#include "qpid/broker/OwnershipToken.h" + +namespace qpid { +namespace management { +class ManagementObject; +} +namespace broker { +class Broker; +namespace amqp { +class ManagedConnection; + +class ManagedSession : public qpid::management::Manageable, public OwnershipToken +{ + public: + ManagedSession(Broker& broker, ManagedConnection& parent, const std::string id); + virtual ~ManagedSession(); + qpid::management::ManagementObject::shared_ptr GetManagementObject() const; + bool isLocal(const ConnectionToken* t) const; + void incomingMessageReceived(); + void incomingMessageAccepted(); + void incomingMessageRejected(); + void outgoingMessageSent(); + void outgoingMessageAccepted(); + void outgoingMessageRejected(); + private: + ManagedConnection& parent; + const std::string id; + qmf::org::apache::qpid::broker::Session::shared_ptr session; + size_t unacked; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDSESSION_H*/ diff --git a/cpp/src/qpid/broker/amqp/Message.cpp b/cpp/src/qpid/broker/amqp/Message.cpp new file mode 100644 index 0000000000..a4c346e131 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Message.cpp @@ -0,0 +1,264 @@ +/* + * + * 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 "Message.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/Buffer.h" +#include <string.h> + +namespace qpid { +namespace broker { +namespace amqp { + +namespace { +std::string empty; +} + +std::string Message::getRoutingKey() const +{ + std::string v; + v.assign(subject.data, subject.size); + return v; +} +std::string Message::getUserId() const +{ + std::string v; + v.assign(userId.data, userId.size); + return v; +} + +bool Message::isPersistent() const +{ + return durable && durable.get(); +} +bool Message::getTtl(uint64_t& t) const +{ + if (!ttl) { + return false; + } else { + t = ttl.get(); + return true; + } +} + +uint8_t Message::getPriority() const +{ + if (!priority) return 4; + else return priority.get(); +} + +std::string Message::getPropertyAsString(const std::string& /*key*/) const { return empty; } +std::string Message::getAnnotationAsString(const std::string& /*key*/) const { return empty; } +void Message::processProperties(MapHandler&) const {} + +//getContentSize() is primarily used in stats about the number of +//bytes enqueued/dequeued etc, not sure whether this is the right name +//and whether it should indeed only be the content that is thus +//measured +uint64_t Message::getContentSize() const { return data.size(); } +//getContent() is used primarily for decoding qmf messages in management and ha +std::string Message::getContent() const { return empty; } + +Message::Message(size_t size) : data(size) +{ + deliveryAnnotations.init(); + messageAnnotations.init(); + bareMessage.init(); + + userId.init(); + to.init(); + subject.init(); + replyTo.init(); + contentType.init(); + contentEncoding.init(); + + applicationProperties.init(); + body.init(); + footer.init(); +} +char* Message::getData() { return &data[0]; } +const char* Message::getData() const { return &data[0]; } +size_t Message::getSize() const { return data.size(); } + +qpid::amqp::MessageId Message::getMessageId() const +{ + return messageId; +} +qpid::amqp::CharSequence Message::getReplyTo() const +{ + return replyTo; +} +qpid::amqp::MessageId Message::getCorrelationId() const +{ + return correlationId; +} +qpid::amqp::CharSequence Message::getContentType() const +{ + return contentType; +} +qpid::amqp::CharSequence Message::getContentEncoding() const +{ + return contentEncoding; +} + +qpid::amqp::CharSequence Message::getDeliveryAnnotations() const +{ + return deliveryAnnotations; +} +qpid::amqp::CharSequence Message::getMessageAnnotations() const +{ + return messageAnnotations; +} +qpid::amqp::CharSequence Message::getApplicationProperties() const +{ + return applicationProperties; +} +qpid::amqp::CharSequence Message::getBareMessage() const +{ + return bareMessage; +} +qpid::amqp::CharSequence Message::getBody() const +{ + return body; +} +qpid::amqp::CharSequence Message::getFooter() const +{ + return footer; +} + +void Message::scan() +{ + qpid::amqp::Decoder decoder(getData(), getSize()); + decoder.read(*this); + bareMessage = qpid::amqp::MessageReader::getBareMessage(); + if (bareMessage.data && !bareMessage.size) { + bareMessage.size = getSize() - (bareMessage.data - getData()); + } +} + +const Message& Message::get(const qpid::broker::Message& message) +{ + const Message* m = dynamic_cast<const Message*>(&message.getEncoding()); + if (!m) throw qpid::Exception("Translation not yet implemented!!"); + return *m; +} + +void Message::onDurable(bool b) { durable = b; } +void Message::onPriority(uint8_t i) { priority = i; } +void Message::onTtl(uint32_t i) { ttl = i; } +void Message::onFirstAcquirer(bool b) { firstAcquirer = b; } +void Message::onDeliveryCount(uint32_t i) { deliveryCount = i; } + +void Message::onMessageId(uint64_t v) { messageId.set(v); } +void Message::onMessageId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { messageId.set(v, t); } +void Message::onUserId(const qpid::amqp::CharSequence& v) { userId = v; } +void Message::onTo(const qpid::amqp::CharSequence& v) { to = v; } +void Message::onSubject(const qpid::amqp::CharSequence& v) { subject = v; } +void Message::onReplyTo(const qpid::amqp::CharSequence& v) { replyTo = v; } +void Message::onCorrelationId(uint64_t v) { correlationId.set(v); } +void Message::onCorrelationId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { correlationId.set(v, t);} +void Message::onContentType(const qpid::amqp::CharSequence& v) { contentType = v; } +void Message::onContentEncoding(const qpid::amqp::CharSequence& v) { contentEncoding = v; } +void Message::onAbsoluteExpiryTime(int64_t) {} +void Message::onCreationTime(int64_t) {} +void Message::onGroupId(const qpid::amqp::CharSequence&) {} +void Message::onGroupSequence(uint32_t) {} +void Message::onReplyToGroupId(const qpid::amqp::CharSequence&) {} + +void Message::onApplicationProperties(const qpid::amqp::CharSequence& v) { applicationProperties = v; } +void Message::onDeliveryAnnotations(const qpid::amqp::CharSequence& v) { deliveryAnnotations = v; } +void Message::onMessageAnnotations(const qpid::amqp::CharSequence& v) { messageAnnotations = v; } +void Message::onBody(const qpid::amqp::CharSequence& v, const qpid::amqp::Descriptor&) { body = v; } +void Message::onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&) {} +void Message::onFooter(const qpid::amqp::CharSequence& v) { footer = v; } + + +//PersistableMessage interface: +void Message::encode(framing::Buffer& buffer) const +{ + buffer.putLong(0);//4-byte format indicator + buffer.putRawData((const uint8_t*) getData(), getSize()); + QPID_LOG(debug, "Encoded 1.0 message of " << getSize() << " bytes, including " << bareMessage.size << " bytes of 'bare message'"); +} +uint32_t Message::encodedSize() const +{ + return 4/*format indicator*/ + data.size(); +} +//in 1.0 the binary header/content makes less sense and in any case +//the functionality that split originally supported (i.e. lazy-loaded +//messages) is no longer in use; for 1.0 we therefore treat the whole +//content as 'header' and load it in the first stage. +uint32_t Message::encodedHeaderSize() const +{ + return encodedSize(); +} +void Message::decodeHeader(framing::Buffer& buffer) +{ + if (buffer.available() != getSize()) { + QPID_LOG(warning, "1.0 Message buffer was " << data.size() << " bytes, but " << buffer.available() << " bytes are available. Resizing."); + data.resize(buffer.available()); + } + buffer.getRawData((uint8_t*) getData(), getSize()); + scan(); + QPID_LOG(debug, "Decoded 1.0 message of " << getSize() << " bytes, including " << bareMessage.size << " bytes of 'bare message'"); +} +void Message::decodeContent(framing::Buffer& /*buffer*/) {} + +boost::intrusive_ptr<PersistableMessage> Message::merge(const std::map<std::string, qpid::types::Variant>& annotations) const +{ + //message- or delivery- annotations? would have to determine that from the name, for now assume always message-annotations + size_t extra = 0; + if (messageAnnotations) { + //TODO: actual merge required + } else { + //add whole new section + extra = qpid::amqp::MessageEncoder::getEncodedSize(annotations, true); + } + boost::intrusive_ptr<Message> copy(new Message(data.size()+extra)); + size_t position(0); + if (deliveryAnnotations) { + ::memcpy(©->data[position], deliveryAnnotations.data, deliveryAnnotations.size); + position += deliveryAnnotations.size; + } + if (messageAnnotations) { + //TODO: actual merge required + ::memcpy(©->data[position], messageAnnotations.data, messageAnnotations.size); + position += messageAnnotations.size; + } else { + qpid::amqp::MessageEncoder encoder(©->data[position], extra); + encoder.writeMap(annotations, &qpid::amqp::message::MESSAGE_ANNOTATIONS, true); + position += extra; + } + if (bareMessage) { + ::memcpy(©->data[position], bareMessage.data, bareMessage.size); + position += bareMessage.size; + } + if (footer) { + ::memcpy(©->data[position], footer.data, footer.size); + position += footer.size; + } + copy->scan(); + return copy; +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Message.h b/cpp/src/qpid/broker/amqp/Message.h new file mode 100644 index 0000000000..cc3406f72a --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Message.h @@ -0,0 +1,148 @@ +#ifndef QPID_BROKER_AMQP_MESSAGE_H +#define QPID_BROKER_AMQP_MESSAGE_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/Message.h" +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/MessageId.h" +#include "qpid/amqp/MessageReader.h" +#include <boost/optional.hpp> + +namespace qpid { +namespace framing { +class Buffer; +} +namespace broker { +namespace amqp { + +/** + * Represents an AMQP 1.0 format message + */ +class Message : public qpid::broker::Message::Encoding, private qpid::amqp::MessageReader, public qpid::broker::PersistableMessage +{ + public: + //Encoding interface: + std::string getRoutingKey() const; + bool isPersistent() const; + uint8_t getPriority() const; + uint64_t getContentSize() const; + std::string getPropertyAsString(const std::string& key) const; + std::string getAnnotationAsString(const std::string& key) const; + bool getTtl(uint64_t&) const; + std::string getContent() const; + void processProperties(MapHandler&) const; + std::string getUserId() const; + + qpid::amqp::MessageId getMessageId() const; + qpid::amqp::CharSequence getReplyTo() const; + qpid::amqp::MessageId getCorrelationId() const; + qpid::amqp::CharSequence getContentType() const; + qpid::amqp::CharSequence getContentEncoding() const; + + qpid::amqp::CharSequence getDeliveryAnnotations() const; + qpid::amqp::CharSequence getMessageAnnotations() const; + qpid::amqp::CharSequence getApplicationProperties() const; + qpid::amqp::CharSequence getBareMessage() const; + qpid::amqp::CharSequence getBody() const; + qpid::amqp::CharSequence getFooter() const; + + Message(size_t size); + char* getData(); + const char* getData() const; + size_t getSize() const; + void scan(); + + //PersistableMessage interface: + void encode(framing::Buffer& buffer) const; + uint32_t encodedSize() const; + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer); + uint32_t encodedHeaderSize() const; + boost::intrusive_ptr<PersistableMessage> merge(const std::map<std::string, qpid::types::Variant>& annotations) const; + + static const Message& get(const qpid::broker::Message&); + private: + std::vector<char> data; + + //header: + boost::optional<bool> durable; + boost::optional<uint8_t> priority; + boost::optional<uint32_t> ttl; + boost::optional<bool> firstAcquirer; + boost::optional<uint32_t> deliveryCount; + //annotations: + qpid::amqp::CharSequence deliveryAnnotations; + qpid::amqp::CharSequence messageAnnotations; + + qpid::amqp::CharSequence bareMessage;//properties, application-properties and content + //properties: + qpid::amqp::MessageId messageId; + qpid::amqp::CharSequence userId; + qpid::amqp::CharSequence to; + qpid::amqp::CharSequence subject; + qpid::amqp::CharSequence replyTo; + qpid::amqp::MessageId correlationId; + qpid::amqp::CharSequence contentType; + qpid::amqp::CharSequence contentEncoding; + + //application-properties: + qpid::amqp::CharSequence applicationProperties; + + //body: + qpid::amqp::CharSequence body; + + //footer: + qpid::amqp::CharSequence footer; + + //header: + void onDurable(bool b); + void onPriority(uint8_t i); + void onTtl(uint32_t i); + void onFirstAcquirer(bool b); + void onDeliveryCount(uint32_t i); + //properties: + void onMessageId(uint64_t); + void onMessageId(const qpid::amqp::CharSequence&, qpid::types::VariantType); + void onUserId(const qpid::amqp::CharSequence& v); + void onTo(const qpid::amqp::CharSequence& v); + void onSubject(const qpid::amqp::CharSequence& v); + void onReplyTo(const qpid::amqp::CharSequence& v); + void onCorrelationId(uint64_t); + void onCorrelationId(const qpid::amqp::CharSequence&, qpid::types::VariantType); + void onContentType(const qpid::amqp::CharSequence& v); + void onContentEncoding(const qpid::amqp::CharSequence& v); + void onAbsoluteExpiryTime(int64_t i); + void onCreationTime(int64_t); + void onGroupId(const qpid::amqp::CharSequence&); + void onGroupSequence(uint32_t); + void onReplyToGroupId(const qpid::amqp::CharSequence&); + + void onApplicationProperties(const qpid::amqp::CharSequence&); + void onDeliveryAnnotations(const qpid::amqp::CharSequence&); + void onMessageAnnotations(const qpid::amqp::CharSequence&); + void onBody(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor&); + void onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&); + void onFooter(const qpid::amqp::CharSequence&); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MESSAGE_H*/ diff --git a/cpp/src/qpid/broker/amqp/NodeProperties.cpp b/cpp/src/qpid/broker/amqp/NodeProperties.cpp new file mode 100644 index 0000000000..eea7612cb9 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/NodeProperties.cpp @@ -0,0 +1,179 @@ +/* + * + * 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/amqp/NodeProperties.h" +#include "qpid/broker/amqp/DataReader.h" +#include "qpid/amqp/CharSequence.h" +#include "qpid/types/Variant.h" +#include "qpid/broker/QueueSettings.h" +#include "qpid/log/Statement.h" + +using qpid::amqp::CharSequence; +using qpid::amqp::Descriptor; + +namespace qpid { +namespace broker { +namespace amqp { +namespace { +//distribution modes: +const std::string MOVE("move"); +const std::string COPY("copy"); +const std::string SUPPORTED_DIST_MODES("supported-dist-modes"); + +//AMQP 0-10 standard parameters: +const std::string DURABLE("durable"); +const std::string AUTO_DELETE("auto-delete"); +const std::string ALTERNATE_EXCHANGE("alternate-exchange"); +const std::string EXCHANGE_TYPE("exchange-type"); +} + +NodeProperties::NodeProperties() : queue(true), durable(false), autoDelete(false), exchangeType("topic") {} + +void NodeProperties::read(pn_data_t* data) +{ + DataReader reader(*this); + reader.read(data); +} + +void NodeProperties::process(const std::string& key, const qpid::types::Variant& value) +{ + QPID_LOG(notice, "Processing node property " << key << " = " << value); + if (key == SUPPORTED_DIST_MODES) { + if (value == MOVE) queue = true; + else if (value == COPY) queue = false; + } else if (key == DURABLE) { + durable = value; + } else if (key == AUTO_DELETE) { + autoDelete = value; + } else if (key == ALTERNATE_EXCHANGE) { + alternateExchange = value.asString(); + } else if (key == EXCHANGE_TYPE) { + exchangeType = value.asString(); + } else { + properties[key] = value; + } +} + +void NodeProperties::onNullValue(const CharSequence& key, const Descriptor*) +{ + process(key.str(), qpid::types::Variant()); +} + +void NodeProperties::onBooleanValue(const CharSequence& key, bool value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onUByteValue(const CharSequence& key, uint8_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onUShortValue(const CharSequence& key, uint16_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onUIntValue(const CharSequence& key, uint32_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onULongValue(const CharSequence& key, uint64_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onByteValue(const CharSequence& key, int8_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onShortValue(const CharSequence& key, int16_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onIntValue(const CharSequence& key, int32_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onLongValue(const CharSequence& key, int64_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onFloatValue(const CharSequence& key, float value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onDoubleValue(const CharSequence& key, double value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onUuidValue(const CharSequence& key, const CharSequence& value, const Descriptor*) +{ + process(key.str(), value.str()); +} + +void NodeProperties::onTimestampValue(const CharSequence& key, int64_t value, const Descriptor*) +{ + process(key.str(), value); +} + +void NodeProperties::onStringValue(const CharSequence& key, const CharSequence& value, const Descriptor*) +{ + process(key.str(), value.str()); +} + +void NodeProperties::onSymbolValue(const CharSequence& key, const CharSequence& value, const Descriptor*) +{ + process(key.str(), value.str()); +} + +QueueSettings NodeProperties::getQueueSettings() +{ + QueueSettings settings(durable, autoDelete); + qpid::types::Variant::Map unused; + settings.populate(properties, unused); + return settings; +} + +bool NodeProperties::isQueue() const +{ + return queue; +} +bool NodeProperties::isDurable() const +{ + return durable; +} +std::string NodeProperties::getExchangeType() const +{ + return exchangeType; +} +std::string NodeProperties::getAlternateExchange() const +{ + return alternateExchange; +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/NodeProperties.h b/cpp/src/qpid/broker/amqp/NodeProperties.h new file mode 100644 index 0000000000..b81d1d712c --- /dev/null +++ b/cpp/src/qpid/broker/amqp/NodeProperties.h @@ -0,0 +1,71 @@ +#ifndef QPID_BROKER_AMQP_NODEPROPERTIES_H +#define QPID_BROKER_AMQP_NODEPROPERTIES_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MapReader.h" +#include "qpid/types/Variant.h" + +struct pn_data_t; +namespace qpid { +namespace broker { +struct QueueSettings; +namespace amqp { + +class NodeProperties : public qpid::amqp::MapReader +{ + public: + NodeProperties(); + void read(pn_data_t*); + void onNullValue(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*); + void onBooleanValue(const qpid::amqp::CharSequence&, bool, const qpid::amqp::Descriptor*); + void onUByteValue(const qpid::amqp::CharSequence&, uint8_t, const qpid::amqp::Descriptor*); + void onUShortValue(const qpid::amqp::CharSequence&, uint16_t, const qpid::amqp::Descriptor*); + void onUIntValue(const qpid::amqp::CharSequence&, uint32_t, const qpid::amqp::Descriptor*); + void onULongValue(const qpid::amqp::CharSequence&, uint64_t, const qpid::amqp::Descriptor*); + void onByteValue(const qpid::amqp::CharSequence&, int8_t, const qpid::amqp::Descriptor*); + void onShortValue(const qpid::amqp::CharSequence&, int16_t, const qpid::amqp::Descriptor*); + void onIntValue(const qpid::amqp::CharSequence&, int32_t, const qpid::amqp::Descriptor*); + void onLongValue(const qpid::amqp::CharSequence&, int64_t, const qpid::amqp::Descriptor*); + void onFloatValue(const qpid::amqp::CharSequence&, float, const qpid::amqp::Descriptor*); + void onDoubleValue(const qpid::amqp::CharSequence&, double, const qpid::amqp::Descriptor*); + void onUuidValue(const qpid::amqp::CharSequence&, const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*); + void onTimestampValue(const qpid::amqp::CharSequence&, int64_t, const qpid::amqp::Descriptor*); + void onStringValue(const qpid::amqp::CharSequence&, const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*); + void onSymbolValue(const qpid::amqp::CharSequence&, const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor*); + bool isQueue() const; + QueueSettings getQueueSettings(); + bool isDurable() const; + std::string getExchangeType() const; + std::string getAlternateExchange() const; + private: + bool queue; + bool durable; + bool autoDelete; + std::string exchangeType; + std::string alternateExchange; + qpid::types::Variant::Map properties; + + void process(const std::string&, const qpid::types::Variant&); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_NODEPROPERTIES_H*/ diff --git a/cpp/src/qpid/broker/amqp/Outgoing.cpp b/cpp/src/qpid/broker/amqp/Outgoing.cpp new file mode 100644 index 0000000000..9605cacac1 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Outgoing.cpp @@ -0,0 +1,244 @@ +/* + * + * 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/amqp/Outgoing.h" +#include "qpid/broker/amqp/Header.h" +#include "qpid/broker/amqp/Translation.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/TopicKeyNode.h" +#include "qpid/sys/OutputControl.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { +namespace amqp { + +Outgoing::Outgoing(Broker& broker, boost::shared_ptr<Queue> q, pn_link_t* l, ManagedSession& session, qpid::sys::OutputControl& o, bool topic) + : Consumer(pn_link_name(l), /*FIXME*/CONSUMER), + ManagedOutgoingLink(broker, *q, session, pn_link_name(l), topic), + exclusive(topic), + queue(q), deliveries(5000), link(l), out(o), + current(0), outstanding(0), + buffer(1024)/*used only for header at present*/ +{ + for (size_t i = 0 ; i < deliveries.capacity(); ++i) { + deliveries[i].init(i); + } +} + +void Outgoing::init() +{ + queue->consume(shared_from_this(), exclusive);//may throw exception +} + +bool Outgoing::dispatch() +{ + QPID_LOG(trace, "Dispatching to " << getName() << ": " << pn_link_credit(link)); + if (canDeliver()) { + if (queue->dispatch(shared_from_this())) { + return true; + } else { + pn_link_drained(link); + QPID_LOG(debug, "No message available on " << queue->getName()); + } + } else { + QPID_LOG(debug, "Can't deliver to " << getName() << " from " << queue->getName() << ": " << pn_link_credit(link)); + } + return false; +} + +void Outgoing::write(const char* data, size_t size) +{ + pn_link_send(link, data, size); +} + +void Outgoing::handle(pn_delivery_t* delivery) +{ + pn_delivery_tag_t tag = pn_delivery_tag(delivery); + size_t i = *reinterpret_cast<const size_t*>(tag.bytes); + Record& r = deliveries[i]; + if (pn_delivery_writable(delivery)) { + assert(r.msg); + assert(!r.delivery); + r.delivery = delivery; + //write header + qpid::amqp::MessageEncoder encoder(&buffer[0], buffer.size()); + encoder.writeHeader(Header(r.msg)); + write(&buffer[0], encoder.getPosition()); + Translation t(r.msg); + t.write(*this); + if (pn_link_advance(link)) { + --outstanding; + outgoingMessageSent(); + QPID_LOG(debug, "Sent message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); + } else { + QPID_LOG(error, "Failed to send message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); + } + } + if (pn_delivery_updated(delivery)) { + assert(r.delivery == delivery); + r.disposition = pn_delivery_remote_state(delivery); + if (r.disposition) { + switch (r.disposition) { + case PN_ACCEPTED: + //TODO: only if consuming + queue->dequeue(0, r.cursor); + outgoingMessageAccepted(); + break; + case PN_REJECTED: + queue->reject(r.cursor); + outgoingMessageRejected(); + break; + case PN_RELEASED: + queue->release(r.cursor, false);//TODO: for PN_RELEASED, delivery count should not be incremented + outgoingMessageRejected();//TODO: not quite true... + break; + case PN_MODIFIED: + queue->release(r.cursor, true);//TODO: proper handling of modified + outgoingMessageRejected();//TODO: not quite true... + break; + default: + QPID_LOG(warning, "Unhandled disposition: " << r.disposition); + } + //TODO: ony settle once any dequeue on store has completed + pn_delivery_settle(delivery); + r.reset(); + } + } +} + +bool Outgoing::canDeliver() +{ + return deliveries[current].delivery == 0 && pn_link_credit(link) > outstanding; +} + +void Outgoing::detached() +{ + QPID_LOG(debug, "Detaching outgoing link from " << queue->getName()); + queue->cancel(shared_from_this()); + //TODO: release in a clearer order? + for (size_t i = 0 ; i < deliveries.capacity(); ++i) { + if (deliveries[i].msg) queue->release(deliveries[i].cursor, true); + } + Queue::tryAutoDelete(*queue->getBroker(), queue, "", ""); +} + +//Consumer interface: +bool Outgoing::deliver(const QueueCursor& cursor, const qpid::broker::Message& msg) +{ + Record& r = deliveries[current++]; + if (current >= deliveries.capacity()) current = 0; + r.cursor = cursor; + r.msg = msg; + pn_delivery(link, r.tag); + QPID_LOG(debug, "Requested delivery of " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); + ++outstanding; + return true; +} + +void Outgoing::notify() +{ + QPID_LOG(trace, "Notification received for " << queue->getName()); + out.activateOutput(); +} + +bool Outgoing::accept(const qpid::broker::Message&) +{ + return true; +} + +void Outgoing::setSubjectFilter(const std::string& f) +{ + subjectFilter = f; +} + +namespace { + +bool match(TokenIterator& filter, TokenIterator& target) +{ + bool wild = false; + while (!filter.finished()) + { + if (filter.match1('*')) { + if (target.finished()) return false; + //else move to next word in filter target + filter.next(); + target.next(); + } else if (filter.match1('#')) { + // i.e. filter word is '#' which can match a variable number of words in the target + filter.next(); + if (filter.finished()) return true; + else if (target.finished()) return false; + wild = true; + } else { + //filter word needs to match target exactly + if (target.finished()) return false; + std::string word; + target.pop(word); + if (filter.match(word)) { + wild = false; + filter.next(); + } else if (!wild) { + return false; + } + } + } + return target.finished(); +} +bool match(const std::string& filter, const std::string& target) +{ + TokenIterator lhs(filter); + TokenIterator rhs(target); + return match(lhs, rhs); +} +} + +bool Outgoing::filter(const qpid::broker::Message& m) +{ + return subjectFilter.empty() || subjectFilter == m.getRoutingKey() || match(subjectFilter, m.getRoutingKey()); +} + +void Outgoing::cancel() {} + +void Outgoing::acknowledged(const qpid::broker::DeliveryRecord&) {} + +qpid::broker::OwnershipToken* Outgoing::getSession() +{ + return 0; +} + +Outgoing::Record::Record() : delivery(0), disposition(0), index(0) {} +void Outgoing::Record::init(size_t i) +{ + index = i; + tag.bytes = reinterpret_cast<const char*>(&index); + tag.size = sizeof(index); +} +void Outgoing::Record::reset() +{ + cursor = QueueCursor(); + msg = qpid::broker::Message(); + delivery = 0; + disposition = 0; +} + + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Outgoing.h b/cpp/src/qpid/broker/amqp/Outgoing.h new file mode 100644 index 0000000000..a8450a48cf --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Outgoing.h @@ -0,0 +1,108 @@ +#ifndef QPID_BROKER_AMQP1_OUTGOING_H +#define QPID_BROKER_AMQP1_OUTGOING_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/amqp/Message.h" +#include "qpid/broker/amqp/ManagedOutgoingLink.h" +#include "qpid/broker/Consumer.h" +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace sys { +class OutputControl; +} +namespace broker { +class Broker; +class Queue; +namespace amqp { +class ManagedSession; +template <class T> +class CircularArray +{ + public: + CircularArray(size_t l) : limit(l), data(new T[limit]) {} + T& operator[](size_t i) { return data[i]; } + size_t capacity() { return limit; } + ~CircularArray() { delete [] data; } + private: + const size_t limit; + T* const data; + size_t next; +}; + +/** + * + */ +class Outgoing : public qpid::broker::Consumer, public boost::enable_shared_from_this<Outgoing>, public ManagedOutgoingLink +{ + public: + Outgoing(Broker&,boost::shared_ptr<Queue> q, pn_link_t* l, ManagedSession&, qpid::sys::OutputControl& o, bool topic); + void setSubjectFilter(const std::string&); + void init(); + bool dispatch(); + void write(const char* data, size_t size); + void handle(pn_delivery_t* delivery); + bool canDeliver(); + void detached(); + + //Consumer interface: + bool deliver(const QueueCursor& cursor, const qpid::broker::Message& msg); + void notify(); + bool accept(const qpid::broker::Message&); + bool filter(const qpid::broker::Message&); + void cancel(); + void acknowledged(const qpid::broker::DeliveryRecord&); + qpid::broker::OwnershipToken* getSession(); + + private: + + struct Record + { + QueueCursor cursor; + qpid::broker::Message msg; + pn_delivery_t* delivery; + int disposition; + size_t index; + pn_delivery_tag_t tag; + + Record(); + void init(size_t i); + void reset(); + }; + + const bool exclusive; + boost::shared_ptr<Queue> queue; + CircularArray<Record> deliveries; + pn_link_t* link; + qpid::sys::OutputControl& out; + size_t current; + int outstanding; + std::vector<char> buffer; + std::string subjectFilter; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_OUTGOING_H*/ diff --git a/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp b/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp new file mode 100644 index 0000000000..711592257c --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/Plugin.h" +#include "qpid/SaslFactory.h" +#include "qpid/NullSaslServer.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Protocol.h" +#include "qpid/broker/RecoverableMessage.h" +#include "qpid/broker/RecoverableMessageImpl.h" +#include "qpid/broker/amqp/Connection.h" +#include "qpid/broker/amqp/Message.h" +#include "qpid/broker/amqp/Sasl.h" +#include "qpid/broker/amqp/Translation.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { +namespace amqp { + +class ProtocolImpl : public Protocol +{ + public: + ProtocolImpl(Broker& b) : broker(b) {} + qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const qpid::broker::Message&); + boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&); + private: + Broker& broker; +}; + +struct ProtocolPlugin : public Plugin +{ + void earlyInitialize(Plugin::Target& target) + { + //need to register protocol before recovery from store + broker::Broker* broker = dynamic_cast<qpid::broker::Broker*>(&target); + if (broker) { + broker->getProtocolRegistry().add("AMQP 1.0", new ProtocolImpl(*broker)); + } + } + + void initialize(Plugin::Target&) {} +}; + +ProtocolPlugin instance; // Static initialization + +qpid::sys::ConnectionCodec* ProtocolImpl::create(const qpid::framing::ProtocolVersion& v, qpid::sys::OutputControl& out, const std::string& id, const qpid::sys::SecuritySettings& external) +{ + if (v == qpid::framing::ProtocolVersion(1, 0)) { + if (v.getProtocol() == qpid::framing::ProtocolVersion::SASL) { + if (broker.getOptions().auth) { + QPID_LOG(info, "Using AMQP 1.0 (with SASL layer)"); + return new qpid::broker::amqp::Sasl(out, id, broker, qpid::SaslFactory::getInstance().createServer(broker.getOptions().realm, broker.getOptions().requireEncrypted, external)); + } else { + std::auto_ptr<SaslServer> authenticator(new qpid::NullSaslServer(broker.getOptions().realm)); + QPID_LOG(info, "Using AMQP 1.0 (with dummy SASL layer)"); + return new qpid::broker::amqp::Sasl(out, id, broker, authenticator); + } + } else { + if (broker.getOptions().auth) { + throw qpid::Exception("SASL layer required!"); + } else { + QPID_LOG(info, "Using AMQP 1.0 (no SASL layer)"); + return new qpid::broker::amqp::Connection(out, id, broker, false); + } + } + } + return 0; +} + +boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> ProtocolImpl::translate(const qpid::broker::Message& m) +{ + qpid::broker::amqp::Translation t(m); + return t.getTransfer(); +} + +boost::shared_ptr<RecoverableMessage> ProtocolImpl::recover(qpid::framing::Buffer& buffer) +{ + QPID_LOG(debug, "Recovering, checking for 1.0 message format indicator..."); + uint32_t format = buffer.getLong(); + if (format == 0) { + QPID_LOG(debug, "Recovered message IS in 1.0 format"); + //this is a 1.0 format message + boost::intrusive_ptr<qpid::broker::amqp::Message> m(new qpid::broker::amqp::Message(buffer.available())); + m->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(qpid::broker::Message(m, m))); + } else { + QPID_LOG(debug, "Recovered message is NOT in 1.0 format"); + return RecoverableMessage::shared_ptr(); + } +} + + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Sasl.cpp b/cpp/src/qpid/broker/amqp/Sasl.cpp new file mode 100644 index 0000000000..4b89e7b15d --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Sasl.cpp @@ -0,0 +1,167 @@ +/* + * + * 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/amqp/Sasl.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/sys/OutputControl.h" +#include "qpid/sys/SecurityLayer.h" +#include <boost/format.hpp> +#include <vector> + +namespace qpid { +namespace broker { +namespace amqp { + +Sasl::Sasl(qpid::sys::OutputControl& o, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr<qpid::SaslServer> auth) + : qpid::amqp::SaslServer(id), out(o), connection(out, id, broker, true), + authenticator(auth), + state(INCOMPLETE), writeHeader(true), haveOutput(true) +{ + out.activateOutput(); + mechanisms(authenticator->getMechanisms()); +} + +Sasl::~Sasl() {} + +size_t Sasl::decode(const char* buffer, size_t size) +{ + if (state == AUTHENTICATED) { + if (securityLayer.get()) return securityLayer->decode(buffer, size); + else return connection.decode(buffer, size); + } else if (state == INCOMPLETE && size) { + size_t decoded = read(buffer, size); + QPID_LOG(trace, id << " Sasl::decode(" << size << "): " << decoded); + return decoded; + } else { + return 0; + } +} + +size_t Sasl::encode(char* buffer, size_t size) +{ + if (state == AUTHENTICATED) { + if (securityLayer.get()) return securityLayer->encode(buffer, size); + else return connection.encode(buffer, size); + } else { + size_t encoded = 0; + if (writeHeader) { + encoded += writeProtocolHeader(buffer, size); + if (!encoded) return 0; + writeHeader = false; + } + if (encoded < size) { + encoded += write(buffer + encoded, size - encoded); + } + if (state == SUCCESS_PENDING) { + state = AUTHENTICATED; + } else if (state == FAILURE_PENDING) { + state = FAILED; + } else { + haveOutput = (encoded == size); + } + QPID_LOG(trace, id << " Sasl::encode(" << size << "): " << encoded); + return encoded; + } +} + +bool Sasl::canEncode() +{ + if (state == AUTHENTICATED) { + if (securityLayer.get()) return securityLayer->canEncode(); + else return connection.canEncode(); + } else { + return haveOutput; + } +} + +void Sasl::closed() +{ + if (state == AUTHENTICATED) { + connection.closed(); + } else { + QPID_LOG(info, id << " Connection closed prior to authentication completing"); + state = FAILED; + } +} +bool Sasl::isClosed() const +{ + if (state == AUTHENTICATED) { + return connection.isClosed(); + } else { + return state == FAILED; + } +} + +framing::ProtocolVersion Sasl::getVersion() const +{ + return connection.getVersion(); +} +namespace { +const std::string EMPTY; +} + +void Sasl::init(const std::string& mechanism, const std::string* response, const std::string* /*hostname*/) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-INIT(" << mechanism << ", " << (response ? *response : EMPTY) << ")"); + //TODO: what should we do with hostname here? + std::string c; + respond(authenticator->start(mechanism, response, c), c); + connection.setSaslMechanism(mechanism); +} + +void Sasl::response(const std::string* r) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-RESPONSE(" << (r ? *r : EMPTY) << ")"); + std::string c; + respond(authenticator->step(r, c), c); +} + +void Sasl::respond(qpid::SaslServer::Status status, const std::string& chllnge) +{ + switch (status) { + case qpid::SaslServer::OK: + connection.setUserid(authenticator->getUserid()); + completed(true); + //can't set authenticated & failed until we have actually sent the outcome + state = SUCCESS_PENDING; + securityLayer = authenticator->getSecurityLayer(65535); + if (securityLayer.get()) { + QPID_LOG_CAT(info, security, id << " Security layer installed"); + securityLayer->init(&connection); + connection.setSaslSsf(securityLayer->getSsf()); + } + QPID_LOG_CAT(info, security, id << " Authenticated as " << authenticator->getUserid()); + break; + case qpid::SaslServer::FAIL: + completed(false); + state = FAILURE_PENDING; + QPID_LOG_CAT(info, security, id << " Failed to authenticate"); + break; + case qpid::SaslServer::CHALLENGE: + challenge(&chllnge); + QPID_LOG_CAT(info, security, id << " Challenge issued"); + break; + } + haveOutput = true; + out.activateOutput(); +} +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Sasl.h b/cpp/src/qpid/broker/amqp/Sasl.h new file mode 100644 index 0000000000..079128be02 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Sasl.h @@ -0,0 +1,72 @@ +#ifndef QPID_BROKER_AMQP_SASL_H +#define QPID_BROKER_AMQP_SASL_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/amqp/Connection.h" +#include "qpid/SaslServer.h" +#include "qpid/amqp/SaslServer.h" +#include "qpid/sys/ConnectionCodec.h" +#include <memory> +namespace qpid { +namespace sys { +class SecurityLayer; +} +namespace broker { +namespace amqp { + +/** + * An AMQP 1.0 SASL Security Layer for authentication and optionally + * encryption. + */ +class Sasl : public sys::ConnectionCodec, qpid::amqp::SaslServer +{ + public: + Sasl(qpid::sys::OutputControl& out, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr<qpid::SaslServer> authenticator); + ~Sasl(); + + size_t decode(const char* buffer, size_t size); + size_t encode(char* buffer, size_t size); + bool canEncode(); + + void closed(); + bool isClosed() const; + + framing::ProtocolVersion getVersion() const; + private: + qpid::sys::OutputControl& out; + Connection connection; + std::auto_ptr<qpid::sys::SecurityLayer> securityLayer; + std::auto_ptr<qpid::SaslServer> authenticator; + enum { + INCOMPLETE, SUCCESS_PENDING, FAILURE_PENDING, AUTHENTICATED, FAILED + } state; + + bool writeHeader; + bool haveOutput; + + void init(const std::string& mechanism, const std::string* response, const std::string* hostname); + void response(const std::string*); + void respond(qpid::SaslServer::Status status, const std::string& challenge); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_SASL_H*/ diff --git a/cpp/src/qpid/broker/amqp/Session.cpp b/cpp/src/qpid/broker/amqp/Session.cpp new file mode 100644 index 0000000000..fabe609473 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Session.cpp @@ -0,0 +1,332 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Session.h" +#include "Outgoing.h" +#include "Message.h" +#include "ManagedConnection.h" +#include "qpid/broker/AsyncCompletion.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/broker/amqp/Filter.h" +#include "qpid/broker/amqp/NodeProperties.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include <boost/intrusive_ptr.hpp> +#include <boost/format.hpp> +#include <map> +#include <sstream> +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace broker { +namespace amqp { + +class Target +{ + public: + Target(pn_link_t* l) : credit(100), window(0), link(l) {} + virtual ~Target() {} + bool flow(); + bool needFlow(); + virtual void handle(qpid::broker::Message& m) = 0;//TODO: revise this for proper message + protected: + const uint32_t credit; + uint32_t window; + pn_link_t* link; +}; + +class Queue : public Target +{ + public: + Queue(boost::shared_ptr<qpid::broker::Queue> q, pn_link_t* l) : Target(l), queue(q) {} + void handle(qpid::broker::Message& m); + private: + boost::shared_ptr<qpid::broker::Queue> queue; +}; + +class Exchange : public Target +{ + public: + Exchange(boost::shared_ptr<qpid::broker::Exchange> e, pn_link_t* l) : Target(l), exchange(e) {} + void handle(qpid::broker::Message& m); + private: + boost::shared_ptr<qpid::broker::Exchange> exchange; +}; + +Session::Session(pn_session_t* s, qpid::broker::Broker& b, ManagedConnection& c, qpid::sys::OutputControl& o) + : ManagedSession(b, c, (boost::format("%1%") % s).str()), session(s), broker(b), connection(c), out(o), deleted(false) {} + + +Session::ResolvedNode Session::resolve(const std::string name, pn_terminus_t* terminus) +{ + ResolvedNode node; + node.exchange = broker.getExchanges().find(name); + node.queue = broker.getQueues().find(name); + if (!node.queue && !node.exchange && pn_terminus_is_dynamic(terminus)) { + //TODO: handle dynamic creation + //is it a queue or an exchange? + NodeProperties properties; + properties.read(pn_terminus_properties(terminus)); + if (properties.isQueue()) { + node.queue = broker.createQueue(name, properties.getQueueSettings(), this, properties.getAlternateExchange(), connection.getUserid(), connection.getId()).first; + } else { + qpid::framing::FieldTable args; + node.exchange = broker.createExchange(name, properties.getExchangeType(), properties.isDurable(), properties.getAlternateExchange(), + args, connection.getUserid(), connection.getId()).first; + } + } else if (node.queue && node.exchange) { + QPID_LOG_CAT(warning, protocol, "Ambiguous node name; " << name << " could be queue or exchange, assuming queue"); + node.exchange.reset(); + } + return node; +} + +void Session::attach(pn_link_t* link) +{ + if (pn_link_is_sender(link)) { + pn_terminus_t* source = pn_link_remote_source(link); + //i.e a subscription + if (pn_terminus_get_type(source) == PN_UNSPECIFIED) { + throw qpid::Exception("No source specified!");/*invalid-field?*/ + } + std::string name = pn_terminus_get_address(source); + QPID_LOG(debug, "Received attach request for outgoing link from " << name); + pn_terminus_set_address(pn_link_source(link), name.c_str()); + + ResolvedNode node = resolve(name, source); + Filter filter; + filter.read(pn_terminus_filter(source)); + + if (node.queue) { + boost::shared_ptr<Outgoing> q(new Outgoing(broker, node.queue, link, *this, out, false)); + q->init(); + if (filter.hasSubjectFilter()) { + q->setSubjectFilter(filter.getSubjectFilter()); + } + senders[link] = q; + } else if (node.exchange) { + QueueSettings settings(false, true); + //TODO: populate settings from source details when available from engine + boost::shared_ptr<qpid::broker::Queue> queue + = broker.createQueue(name + qpid::types::Uuid(true).str(), settings, this, "", connection.getUserid(), connection.getId()).first; + if (filter.hasSubjectFilter()) { + filter.bind(node.exchange, queue); + filter.write(pn_terminus_filter(pn_link_source(link))); + } else if (node.exchange->getType() == FanOutExchange::typeName) { + node.exchange->bind(queue, std::string(), 0); + } else if (node.exchange->getType() == TopicExchange::typeName) { + node.exchange->bind(queue, "#", 0); + } else { + throw qpid::Exception("Exchange type requires a filter: " + node.exchange->getType());/*not-supported?*/ + } + boost::shared_ptr<Outgoing> q(new Outgoing(broker, queue, link, *this, out, true)); + senders[link] = q; + q->init(); + } else { + pn_terminus_set_type(pn_link_source(link), PN_UNSPECIFIED); + throw qpid::Exception("Node not found: " + name);/*not-found*/ + } + QPID_LOG(debug, "Outgoing link attached"); + } else { + pn_terminus_t* target = pn_link_remote_target(link); + if (pn_terminus_get_type(target) == PN_UNSPECIFIED) { + throw qpid::Exception("No target specified!");/*invalid field?*/ + } + std::string name = pn_terminus_get_address(target); + QPID_LOG(debug, "Received attach request for incoming link to " << name); + pn_terminus_set_address(pn_link_target(link), name.c_str()); + + ResolvedNode node = resolve(name, target); + + if (node.queue) { + boost::shared_ptr<Target> q(new Queue(node.queue, link)); + targets[link] = q; + } else if (node.exchange) { + boost::shared_ptr<Target> e(new Exchange(node.exchange, link)); + targets[link] = e; + } else { + pn_terminus_set_type(pn_link_target(link), PN_UNSPECIFIED); + throw qpid::Exception("Node not found: " + name);/*not-found*/ + } + QPID_LOG(debug, "Incoming link attached"); + } +} + +void Session::detach(pn_link_t* link) +{ + if (pn_link_is_sender(link)) { + Senders::iterator i = senders.find(link); + if (i != senders.end()) { + i->second->detached(); + senders.erase(i); + QPID_LOG(debug, "Outgoing link detached"); + } + } else { + targets.erase(link); + QPID_LOG(debug, "Incoming link detached"); + } +} +namespace { + class Transfer : public qpid::broker::AsyncCompletion::Callback + { + public: + Transfer(pn_delivery_t* d, boost::shared_ptr<Session> s) : delivery(d), session(s) {} + void completed(bool sync) { session->accepted(delivery, sync); } + boost::intrusive_ptr<qpid::broker::AsyncCompletion::Callback> clone() + { + boost::intrusive_ptr<qpid::broker::AsyncCompletion::Callback> copy(new Transfer(delivery, session)); + return copy; + } + private: + pn_delivery_t* delivery; + boost::shared_ptr<Session> session; + }; +} + +void Session::accepted(pn_delivery_t* delivery, bool sync) +{ + if (sync) { + //this is on IO thread + pn_delivery_update(delivery, PN_ACCEPTED); + pn_delivery_settle(delivery);//do we need to check settlement modes/orders? + incomingMessageAccepted(); + } else { + //this is not on IO thread, need to delay processing until on IO thread + qpid::sys::Mutex::ScopedLock l(lock); + if (!deleted) { + completed.push_back(delivery); + out.activateOutput(); + } + } +} + +void Session::incoming(pn_link_t* link, pn_delivery_t* delivery) +{ + pn_delivery_tag_t tag = pn_delivery_tag(delivery); + QPID_LOG(debug, "received delivery: " << std::string(tag.bytes, tag.size)); + boost::intrusive_ptr<Message> received(new Message(pn_delivery_pending(delivery))); + /*ssize_t read = */pn_link_recv(link, received->getData(), received->getSize()); + received->scan(); + pn_link_advance(link); + + qpid::broker::Message message(received, received); + + incomingMessageReceived(); + Targets::iterator target = targets.find(link); + if (target == targets.end()) { + QPID_LOG(error, "Received message on unknown link"); + pn_delivery_update(delivery, PN_REJECTED); + pn_delivery_settle(delivery);//do we need to check settlement modes/orders? + incomingMessageRejected(); + } else { + target->second->handle(message); + received->begin(); + Transfer t(delivery, shared_from_this()); + received->end(t); + if (target->second->needFlow()) out.activateOutput(); + } +} +void Session::outgoing(pn_link_t* link, pn_delivery_t* delivery) +{ + Senders::iterator sender = senders.find(link); + if (sender == senders.end()) { + QPID_LOG(error, "Delivery returned for unknown link"); + } else { + sender->second->handle(delivery); + } +} + +bool Session::dispatch() +{ + bool output(false); + for (Senders::iterator s = senders.begin(); s != senders.end(); ++s) { + if (s->second->dispatch()) output = true; + } + if (completed.size()) { + output = true; + std::deque<pn_delivery_t*> copy; + { + qpid::sys::Mutex::ScopedLock l(lock); + completed.swap(copy); + } + for (std::deque<pn_delivery_t*>::iterator i = copy.begin(); i != copy.end(); ++i) { + accepted(*i, true); + } + } + for (Targets::iterator t = targets.begin(); t != targets.end(); ++t) { + if (t->second->flow()) output = true; + } + + return output; +} + +void Session::close() +{ + for (Senders::iterator i = senders.begin(); i != senders.end(); ++i) { + i->second->detached(); + } + senders.clear(); + targets.clear();//at present no explicit cleanup required for targets + QPID_LOG(debug, "Session closed, all senders cancelled."); + qpid::sys::Mutex::ScopedLock l(lock); + deleted = true; +} + +void Queue::handle(qpid::broker::Message& message) +{ + queue->deliver(message); + --window; +} + +void Exchange::handle(qpid::broker::Message& message) +{ + DeliverableMessage deliverable(message, 0); + exchange->route(deliverable); + --window; +} + +bool Target::flow() +{ + bool issue = window < credit; + if (issue) { + pn_link_flow(link, credit - window);//TODO: proper flow control + window = credit; + } + return issue; +} + +bool Target::needFlow() +{ + return window <= (credit/2); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Session.h b/cpp/src/qpid/broker/amqp/Session.h new file mode 100644 index 0000000000..7dbdaf05fc --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Session.h @@ -0,0 +1,87 @@ +#ifndef QPID_BROKER_AMQP1_SESSION_H +#define QPID_BROKER_AMQP1_SESSION_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/Mutex.h" +#include "qpid/sys/OutputControl.h" +#include "qpid/broker/amqp/ManagedSession.h" +#include <deque> +#include <map> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +struct pn_delivery_t; +struct pn_link_t; +struct pn_session_t; +struct pn_terminus_t; + +namespace qpid { +namespace broker { + +class Broker; +class Exchange; +class Queue; + +namespace amqp { + +class ManagedConnection; +class Outgoing; +class Target; +/** + * + */ +class Session : public ManagedSession, public boost::enable_shared_from_this<Session> +{ + public: + Session(pn_session_t*, qpid::broker::Broker&, ManagedConnection&, qpid::sys::OutputControl&); + void attach(pn_link_t*); + void detach(pn_link_t*); + void incoming(pn_link_t*, pn_delivery_t*); + void outgoing(pn_link_t*, pn_delivery_t*); + bool dispatch(); + void close(); + + //called when a transfer is completly processed (e.g.including stored on disk) + void accepted(pn_delivery_t*, bool sync); + private: + typedef std::map<pn_link_t*, boost::shared_ptr<Outgoing> > Senders; + typedef std::map<pn_link_t*, boost::shared_ptr<Target> > Targets; + pn_session_t* session; + qpid::broker::Broker& broker; + ManagedConnection& connection; + qpid::sys::OutputControl& out; + Targets targets; + Senders senders; + std::deque<pn_delivery_t*> completed; + bool deleted; + qpid::sys::Mutex lock; + struct ResolvedNode + { + boost::shared_ptr<qpid::broker::Exchange> exchange; + boost::shared_ptr<qpid::broker::Queue> queue; + }; + + ResolvedNode resolve(const std::string name, pn_terminus_t* terminus); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_SESSION_H*/ diff --git a/cpp/src/qpid/broker/amqp/Translation.cpp b/cpp/src/qpid/broker/amqp/Translation.cpp new file mode 100644 index 0000000000..ca2094b965 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Translation.cpp @@ -0,0 +1,241 @@ +/* + * + * 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/amqp/Translation.h" +#include "qpid/broker/amqp/Outgoing.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/amqp_0_10/Codecs.h" +#include "qpid/types/Variant.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include <boost/lexical_cast.hpp> + +namespace qpid { +namespace broker { +namespace amqp { +namespace { + +const std::string EMPTY; +const std::string FORWARD_SLASH("/"); + +std::string translate(const qpid::framing::ReplyTo r) +{ + if (r.hasExchange()) { + if (r.hasRoutingKey()) return r.getExchange() + FORWARD_SLASH + r.getRoutingKey(); + else return r.getExchange(); + } else return r.getRoutingKey(); +} +std::string translate(const qpid::amqp::CharSequence& chars) +{ + if (chars.data && chars.size) return std::string(chars.data, chars.size); + else return EMPTY; +} +bool setMessageId(qpid::framing::MessageProperties& m, const qpid::amqp::CharSequence& chars) +{ + if (chars.data && chars.size) { + if (chars.size == 16) { + m.setMessageId(qpid::framing::Uuid(chars.data)); + return true; + } else { + std::istringstream in(translate(chars)); + qpid::framing::Uuid uuid; + in >> uuid; + if (!in.fail()) { + m.setMessageId(uuid); + return true; + } + } + } + return false; +} +class Properties_0_10 : public qpid::amqp::MessageEncoder::Properties +{ + public: + bool hasMessageId() const { return messageProperties && messageProperties->hasMessageId(); } + std::string getMessageId() const { return messageProperties ? messageProperties->getMessageId().str() : EMPTY; } + bool hasUserId() const { return messageProperties && messageProperties->hasUserId(); } + std::string getUserId() const { return messageProperties ? messageProperties->getUserId() : EMPTY; } + bool hasTo() const { return getDestination().size() || hasSubject(); } + std::string getTo() const { return getDestination().size() ? getDestination() : getSubject(); } + bool hasSubject() const { return deliveryProperties && getDestination().size() && deliveryProperties->hasRoutingKey(); } + std::string getSubject() const { return deliveryProperties && getDestination().size() ? deliveryProperties->getRoutingKey() : EMPTY; } + bool hasReplyTo() const { return messageProperties && messageProperties->hasReplyTo(); } + std::string getReplyTo() const { return messageProperties ? translate(messageProperties->getReplyTo()) : EMPTY; } + bool hasCorrelationId() const { return messageProperties && messageProperties->hasCorrelationId(); } + std::string getCorrelationId() const { return messageProperties ? messageProperties->getCorrelationId() : EMPTY; } + bool hasContentType() const { return messageProperties && messageProperties->hasContentType(); } + std::string getContentType() const { return messageProperties ? messageProperties->getContentType() : EMPTY; } + bool hasContentEncoding() const { return messageProperties && messageProperties->hasContentEncoding(); } + std::string getContentEncoding() const { return messageProperties ? messageProperties->getContentEncoding() : EMPTY; } + bool hasAbsoluteExpiryTime() const { return deliveryProperties && deliveryProperties->hasExpiration(); } + int64_t getAbsoluteExpiryTime() const { return deliveryProperties ? deliveryProperties->getExpiration() : 0; } + bool hasCreationTime() const { return false; } + int64_t getCreationTime() const { return 0; } + bool hasGroupId() const {return false; } + std::string getGroupId() const { return EMPTY; } + bool hasGroupSequence() const { return false; } + uint32_t getGroupSequence() const { return 0; } + bool hasReplyToGroupId() const { return false; } + std::string getReplyToGroupId() const { return EMPTY; } + + const qpid::framing::FieldTable& getApplicationProperties() { return messageProperties->getApplicationHeaders(); } + Properties_0_10(const qpid::broker::amqp_0_10::MessageTransfer& t) : transfer(t), + messageProperties(transfer.getProperties<qpid::framing::MessageProperties>()), + deliveryProperties(transfer.getProperties<qpid::framing::DeliveryProperties>()) + {} + private: + const qpid::broker::amqp_0_10::MessageTransfer& transfer; + const qpid::framing::MessageProperties* messageProperties; + const qpid::framing::DeliveryProperties* deliveryProperties; + + std::string getDestination() const + { + return transfer.getMethod<qpid::framing::MessageTransferBody>()->getDestination(); + } +}; +} + +Translation::Translation(const qpid::broker::Message& m) : original(m) {} + + +boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> Translation::getTransfer() +{ + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> t = + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer>(dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&original.getEncoding())); + if (t) { + return t;//no translation required + } else { + const Message* message = dynamic_cast<const Message*>(&original.getEncoding()); + if (message) { + //translate 1.0 message into 0-10 + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer()); + qpid::framing::AMQFrame method((qpid::framing::MessageTransferBody(qpid::framing::ProtocolVersion(), EMPTY, 0, 0))); + qpid::framing::AMQFrame header((qpid::framing::AMQHeaderBody())); + qpid::framing::AMQFrame content((qpid::framing::AMQContentBody())); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content.setBof(false); + + transfer->getFrames().append(method); + transfer->getFrames().append(header); + + qpid::amqp::CharSequence body = message->getBody(); + content.castBody<qpid::framing::AMQContentBody>()->getData().assign(body.data, body.size); + transfer->getFrames().append(content); + + qpid::framing::MessageProperties* props = + transfer->getFrames().getHeaders()->get<qpid::framing::MessageProperties>(true); + props->setContentLength(body.size); + + qpid::amqp::MessageId mid = message->getMessageId(); + qpid::framing::Uuid uuid; + switch (mid.type) { + case qpid::amqp::MessageId::UUID: + case qpid::amqp::MessageId::BYTES: + if (mid.value.bytes.size == 0) break; + if (setMessageId(*props, mid.value.bytes)) break; + case qpid::amqp::MessageId::ULONG: + QPID_LOG(info, "Skipping message id in translation from 1.0 to 0-10 as it is not a UUID"); + break; + } + + qpid::amqp::MessageId cid = message->getCorrelationId(); + switch (cid.type) { + case qpid::amqp::MessageId::UUID: + assert(cid.value.bytes.size = 16); + props->setCorrelationId(qpid::framing::Uuid(cid.value.bytes.data).str()); + break; + case qpid::amqp::MessageId::BYTES: + if (cid.value.bytes.size) { + props->setCorrelationId(translate(cid.value.bytes)); + } + break; + case qpid::amqp::MessageId::ULONG: + props->setCorrelationId(boost::lexical_cast<std::string>(cid.value.ulong)); + break; + } + // TODO: ReplyTo - there is no way to reliably determine + // the type of the node from just its name, unless we + // query the brokers registries + + if (message->getContentType()) props->setContentType(translate(message->getContentType())); + if (message->getContentEncoding()) props->setContentEncoding(translate(message->getContentEncoding())); + props->setUserId(message->getUserId()); + // TODO: FieldTable applicationHeaders; + qpid::amqp::CharSequence ap = message->getApplicationProperties(); + if (ap) { + qpid::amqp::Decoder d(ap.data, ap.size); + qpid::amqp_0_10::translate(d.readMap(), props->getApplicationHeaders()); + } + + qpid::framing::DeliveryProperties* dp = + transfer->getFrames().getHeaders()->get<qpid::framing::DeliveryProperties>(true); + dp->setPriority(message->getPriority()); + if (message->isPersistent()) dp->setDeliveryMode(2); + if (message->getRoutingKey().size()) dp->setRoutingKey(message->getRoutingKey()); + + return transfer.get(); + } else { + throw qpid::Exception("Could not write message data in AMQP 0-10 format"); + } + } +} + +void Translation::write(Outgoing& out) +{ + const Message* message = dynamic_cast<const Message*>(&original.getEncoding()); + if (message) { + //write annotations + //TODO: merge in any newly added annotations + qpid::amqp::CharSequence deliveryAnnotations = message->getDeliveryAnnotations(); + qpid::amqp::CharSequence messageAnnotations = message->getMessageAnnotations(); + if (deliveryAnnotations.size) out.write(deliveryAnnotations.data, deliveryAnnotations.size); + if (messageAnnotations.size) out.write(messageAnnotations.data, messageAnnotations.size); + //write bare message + qpid::amqp::CharSequence bareMessage = message->getBareMessage(); + if (bareMessage.size) out.write(bareMessage.data, bareMessage.size); + //write footer: + qpid::amqp::CharSequence footer = message->getFooter(); + if (footer.size) out.write(footer.data, footer.size); + } else { + const qpid::broker::amqp_0_10::MessageTransfer* transfer = dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&original.getEncoding()); + if (transfer) { + Properties_0_10 properties(*transfer); + qpid::types::Variant::Map applicationProperties; + qpid::amqp_0_10::translate(properties.getApplicationProperties(), applicationProperties); + std::string content = transfer->getContent(); + size_t size = qpid::amqp::MessageEncoder::getEncodedSize(properties, applicationProperties, content); + std::vector<char> buffer(size); + qpid::amqp::MessageEncoder encoder(&buffer[0], buffer.size()); + encoder.writeProperties(properties); + encoder.writeApplicationProperties(applicationProperties); + encoder.writeBinary(content, &qpid::amqp::message::DATA); + out.write(&buffer[0], encoder.getPosition()); + } else { + QPID_LOG(error, "Could not write message data in AMQP 1.0 format"); + } + } +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Translation.h b/cpp/src/qpid/broker/amqp/Translation.h new file mode 100644 index 0000000000..64d96560e3 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Translation.h @@ -0,0 +1,58 @@ +#ifndef QPID_BROKER_AMQP_TRANSLATION_H +#define QPID_BROKER_AMQP_TRANSLATION_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/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { +class Message; +namespace amqp_0_10 { +class MessageTransfer; +} +namespace amqp { + +class Outgoing; +/** + * + */ +class Translation +{ + public: + Translation(const qpid::broker::Message& message); + + /** + * @returns a pointer to an AMQP 0-10 message transfer suitable + * for sending on an 0-10 session, translating from 1.0 as + * necessary + */ + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> getTransfer(); + /** + * Writes the AMQP 1.0 bare message and any annotations, translating from 0-10 if necessary + */ + void write(Outgoing&); + private: + const qpid::broker::Message& original; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_TRANSLATION_H*/ diff --git a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp index cac4434c48..b78b69b2d6 100644 --- a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp +++ b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp @@ -29,6 +29,7 @@ #include "qpid/framing/TypeFilter.h" #include "qpid/framing/SendContent.h" #include "qpid/log/Statement.h" +#include "boost/lexical_cast.hpp" using namespace qpid::framing; @@ -51,7 +52,12 @@ std::string MessageTransfer::getAnnotationAsString(const std::string& key) const { const qpid::framing::MessageProperties* mp = getProperties<qpid::framing::MessageProperties>(); if (mp && mp->hasApplicationHeaders()) { - return mp->getApplicationHeaders().getAsString(key); + FieldTable::ValuePtr value = mp->getApplicationHeaders().get(key); + if (value) { + if (value->convertsTo<std::string>()) return value->get<std::string>(); + else if (value->convertsTo<int>()) return boost::lexical_cast<std::string>(value->get<int>()); + } + return std::string(); } else { return std::string(); } @@ -116,11 +122,6 @@ void MessageTransfer::computeRequiredCredit() requiredCredit = sum.getSize(); cachedRequiredCredit = true; } -uint32_t MessageTransfer::getRequiredCredit(const qpid::broker::Message& msg) -{ - //TODO: may need to reflect annotations and other modifications in this also - return get(msg).getRequiredCredit(); -} qpid::framing::FrameSet& MessageTransfer::getFrames() { diff --git a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h index 590e389518..9e432235e6 100644 --- a/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h +++ b/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h @@ -109,7 +109,6 @@ class MessageTransfer : public qpid::broker::Message::Encoding, public qpid::bro QPID_BROKER_EXTERN bool isLastQMFResponse(const std::string correlation) const; static bool isImmediateDeliveryRequired(const qpid::broker::Message& message); - static uint32_t getRequiredCredit(const qpid::broker::Message&); static MessageTransfer& get(qpid::broker::Message& message) { return *dynamic_cast<MessageTransfer*>(&message.getEncoding()); } diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp index 40e74be018..c04d037a6e 100644 --- a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp +++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp @@ -23,6 +23,7 @@ // accessing authentication mechanisms, analogous to Cyrus SASL. #include "qpid/broker/Connection.h" +#include "qpid/broker/Broker.h" #include "qpid/log/Statement.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/FieldValue.h" diff --git a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp index 420e04e832..a07afe45ae 100644 --- a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp +++ b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp @@ -1,352 +1,379 @@ -/* - * - * 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/ProtocolFactory.h" - -#include "qpid/Plugin.h" -#include "qpid/broker/Broker.h" -#include "qpid/log/Statement.h" -#include "qpid/sys/AsynchIOHandler.h" -#include "qpid/sys/ConnectionCodec.h" -#include "qpid/sys/Socket.h" -#include "qpid/sys/SocketAddress.h" -#include "qpid/sys/SystemInfo.h" -#include "qpid/sys/windows/SslAsynchIO.h" - -#include <boost/bind.hpp> -#include <boost/ptr_container/ptr_vector.hpp> -#include <memory> - -// security.h needs to see this to distinguish from kernel use. -#define SECURITY_WIN32 -#include <security.h> -#include <Schnlsp.h> -#undef SECURITY_WIN32 - - -namespace qpid { -namespace sys { - -class Timer; - -namespace windows { - -struct SslServerOptions : qpid::Options -{ - std::string certStore; - std::string certStoreLocation; - std::string certName; - uint16_t port; - bool clientAuth; - - SslServerOptions() : qpid::Options("SSL Options"), - certStore("My"), - certStoreLocation("CurrentUser"), - certName("localhost"), - port(5671), - clientAuth(false) - { - qpid::Address me; - if (qpid::sys::SystemInfo::getLocalHostname(me)) - certName = me.host; - - addOptions() - ("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate") - ("ssl-cert-store-location", optValue(certStoreLocation, "NAME"), - "Local store name location for certificates ( CurrentUser | LocalMachine | CurrentService )") - ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use") - ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections") - ("ssl-require-client-authentication", optValue(clientAuth), - "Forces clients to authenticate in order to establish an SSL connection"); - } -}; - -class SslProtocolFactory : public qpid::sys::ProtocolFactory { - boost::ptr_vector<Socket> listeners; - boost::ptr_vector<AsynchAcceptor> acceptors; - Timer& brokerTimer; - uint32_t maxNegotiateTime; - uint16_t listeningPort; - const bool tcpNoDelay; - std::string brokerHost; - const bool clientAuthSelected; - std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor; - ConnectFailedCallback connectFailedCallback; - CredHandle credHandle; - - public: - SslProtocolFactory(const SslServerOptions&, const std::string& host, const std::string& port, - int backlog, bool nodelay, - Timer& timer, uint32_t maxTime); - ~SslProtocolFactory(); - void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*); - void connect(sys::Poller::shared_ptr, const std::string& host, const std::string& port, - sys::ConnectionCodec::Factory*, - ConnectFailedCallback failed); - - uint16_t getPort() const; - bool supports(const std::string& capability); - - private: - void connectFailed(const qpid::sys::Socket&, - int err, - const std::string& msg); - void established(sys::Poller::shared_ptr, - const qpid::sys::Socket&, - sys::ConnectionCodec::Factory*, - bool isClient); -}; - -// Static instance to initialise plugin -static struct SslPlugin : public Plugin { - SslServerOptions options; - - Options* getOptions() { return &options; } - - void earlyInitialize(Target&) { - } - - void initialize(Target& target) { - broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); - // Only provide to a Broker - if (broker) { - try { - const broker::Broker::Options& opts = broker->getOptions(); - ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options, - "", boost::lexical_cast<std::string>(options.port), - opts.connectionBacklog, opts.tcpNoDelay, - broker->getTimer(), opts.maxNegotiateTime)); - QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort()); - broker->registerProtocolFactory("ssl", protocol); - } catch (const std::exception& e) { - QPID_LOG(error, "Failed to initialise SSL listener: " << e.what()); - } - } - } -} sslPlugin; - -SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, - const std::string& host, const std::string& port, - int backlog, bool nodelay, - Timer& timer, uint32_t maxTime) - : brokerTimer(timer), - maxNegotiateTime(maxTime), - tcpNoDelay(nodelay), - clientAuthSelected(options.clientAuth) { - - // Make sure that certificate store is good before listening to sockets - // to avoid having open and listening sockets when there is no cert store - SecInvalidateHandle(&credHandle); - - // Get the certificate for this server. - DWORD flags = 0; - std::string certStoreLocation = options.certStoreLocation; +/*
+ *
+ * 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/ProtocolFactory.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/ConnectionCodec.h"
+#include "qpid/sys/Socket.h"
+#include "qpid/sys/SocketAddress.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/windows/SslAsynchIO.h"
+
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <memory>
+
+// security.h needs to see this to distinguish from kernel use.
+#define SECURITY_WIN32
+#include <security.h>
+#include <Schnlsp.h>
+#undef SECURITY_WIN32
+
+
+namespace qpid {
+namespace sys {
+
+class Timer;
+
+namespace windows {
+
+struct SslServerOptions : qpid::Options
+{
+ std::string certStore;
+ std::string certStoreLocation;
+ std::string certName;
+ uint16_t port;
+ bool clientAuth;
+
+ SslServerOptions() : qpid::Options("SSL Options"),
+ certStore("My"),
+ certStoreLocation("CurrentUser"),
+ certName("localhost"),
+ port(5671),
+ clientAuth(false)
+ {
+ qpid::Address me;
+ if (qpid::sys::SystemInfo::getLocalHostname(me))
+ certName = me.host;
+
+ addOptions()
+ ("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate")
+ ("ssl-cert-store-location", optValue(certStoreLocation, "NAME"),
+ "Local store name location for certificates ( CurrentUser | LocalMachine | CurrentService )")
+ ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use")
+ ("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
+ ("ssl-require-client-authentication", optValue(clientAuth),
+ "Forces clients to authenticate in order to establish an SSL connection");
+ }
+};
+
+class SslProtocolFactory : public qpid::sys::ProtocolFactory {
+ boost::ptr_vector<Socket> listeners;
+ boost::ptr_vector<AsynchAcceptor> acceptors;
+ Timer& brokerTimer;
+ uint32_t maxNegotiateTime;
+ uint16_t listeningPort;
+ const bool tcpNoDelay;
+ std::string brokerHost;
+ const bool clientAuthSelected;
+ std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
+ ConnectFailedCallback connectFailedCallback;
+ CredHandle credHandle;
+
+ public:
+ SslProtocolFactory(const qpid::broker::Broker::Options& opts, const SslServerOptions&, Timer& timer);
+ ~SslProtocolFactory();
+ void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
+ void connect(sys::Poller::shared_ptr, const std::string& name, const std::string& host, const std::string& port,
+ sys::ConnectionCodec::Factory*,
+ ConnectFailedCallback failed);
+
+ uint16_t getPort() const;
+
+ private:
+ void connectFailed(const qpid::sys::Socket&,
+ int err,
+ const std::string& msg);
+ void establishedIncoming(sys::Poller::shared_ptr, const qpid::sys::Socket&, sys::ConnectionCodec::Factory*);
+ void establishedOutgoing(sys::Poller::shared_ptr, const qpid::sys::Socket&, sys::ConnectionCodec::Factory*, std::string& );
+ void establishedCommon(sys::Poller::shared_ptr, sys::AsynchIOHandler*, sys::AsynchIO*, const qpid::sys::Socket&);
+};
+
+// Static instance to initialise plugin
+static struct SslPlugin : public Plugin {
+ SslServerOptions options;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize(Target&) {
+ }
+
+ void initialize(Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ // Only provide to a Broker
+ if (broker) {
+ try {
+ const broker::Broker::Options& opts = broker->getOptions();
+ ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(opts, options, broker->getTimer()));
+ QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
+ broker->registerProtocolFactory("ssl", protocol);
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Failed to initialise SSL listener: " << e.what());
+ }
+ }
+ }
+} sslPlugin;
+
+namespace {
+ // Expand list of Interfaces and addresses to a list of addresses
+ std::vector<std::string> expandInterfaces(const std::vector<std::string>& interfaces) {
+ std::vector<std::string> addresses;
+ // If there are no specific interfaces listed use a single "" to listen on every interface
+ if (interfaces.empty()) {
+ addresses.push_back("");
+ return addresses;
+ }
+ for (unsigned i = 0; i < interfaces.size(); ++i) {
+ const std::string& interface = interfaces[i];
+ if (!(SystemInfo::getInterfaceAddresses(interface, addresses))) {
+ // We don't have an interface of that name -
+ // Check for IPv6 ('[' ']') brackets and remove them
+ // then pass to be looked up directly
+ if (interface[0]=='[' && interface[interface.size()-1]==']') {
+ addresses.push_back(interface.substr(1, interface.size()-2));
+ } else {
+ addresses.push_back(interface);
+ }
+ }
+ }
+ return addresses;
+ }
+}
+
+SslProtocolFactory::SslProtocolFactory(const qpid::broker::Broker::Options& opts, const SslServerOptions& options, Timer& timer)
+ : brokerTimer(timer),
+ maxNegotiateTime(opts.maxNegotiateTime),
+ tcpNoDelay(opts.tcpNoDelay),
+ clientAuthSelected(options.clientAuth) {
+
+ // Make sure that certificate store is good before listening to sockets
+ // to avoid having open and listening sockets when there is no cert store
+ SecInvalidateHandle(&credHandle);
+
+ // Get the certificate for this server.
+ DWORD flags = 0;
+ std::string certStoreLocation = options.certStoreLocation;
std::transform(certStoreLocation.begin(), certStoreLocation.end(), certStoreLocation.begin(), ::tolower);
- if (certStoreLocation == "currentuser") { - flags = CERT_SYSTEM_STORE_CURRENT_USER; - } else if (certStoreLocation == "localmachine") { - flags = CERT_SYSTEM_STORE_LOCAL_MACHINE; - } else if (certStoreLocation == "currentservice") { - flags = CERT_SYSTEM_STORE_CURRENT_SERVICE; - } else { - QPID_LOG(error, "Unrecognised SSL certificate store location: " << options.certStoreLocation - << " - Using default location"); - } - HCERTSTORE certStoreHandle; - certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A, - X509_ASN_ENCODING, - 0, - flags | - CERT_STORE_READONLY_FLAG, - options.certStore.c_str()); - if (!certStoreHandle) - throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError()))); - - PCCERT_CONTEXT certContext; - certContext = ::CertFindCertificateInStore(certStoreHandle, - X509_ASN_ENCODING, - 0, - CERT_FIND_SUBJECT_STR_A, - options.certName.c_str(), - NULL); - if (certContext == NULL) { - int err = ::GetLastError(); - ::CertCloseStore(certStoreHandle, 0); - throw qpid::Exception(QPID_MSG("Locating certificate " << options.certName << " in store " << options.certStore << " " << qpid::sys::strError(GetLastError()))); - throw QPID_WINDOWS_ERROR(err); - } - - SCHANNEL_CRED cred; - memset(&cred, 0, sizeof(cred)); - cred.dwVersion = SCHANNEL_CRED_VERSION; - cred.cCreds = 1; - cred.paCred = &certContext; - SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL, - UNISP_NAME, - SECPKG_CRED_INBOUND, - NULL, - &cred, - NULL, - NULL, - &credHandle, - NULL); - if (status != SEC_E_OK) - throw QPID_WINDOWS_ERROR(status); - ::CertFreeCertificateContext(certContext); - ::CertCloseStore(certStoreHandle, 0); - - // Listen to socket(s) - SocketAddress sa(host, port); - - // We must have at least one resolved address - QPID_LOG(info, "SSL Listening to: " << sa.asString()) - Socket* s = new Socket; - listeningPort = s->listen(sa, backlog); - listeners.push_back(s); - - // Try any other resolved addresses - while (sa.nextAddress()) { - QPID_LOG(info, "SSL Listening to: " << sa.asString()) - Socket* s = new Socket; - s->listen(sa, backlog); - listeners.push_back(s); - } -} - -SslProtocolFactory::~SslProtocolFactory() { - ::FreeCredentialsHandle(&credHandle); -} - -void SslProtocolFactory::connectFailed(const qpid::sys::Socket&, - int err, - const std::string& msg) { - if (connectFailedCallback) - connectFailedCallback(err, msg); -} - -void SslProtocolFactory::established(sys::Poller::shared_ptr poller, - const qpid::sys::Socket& s, - sys::ConnectionCodec::Factory* f, - bool isClient) { - sys::AsynchIOHandler* async = new sys::AsynchIOHandler(s.getFullAddress(), f); - - if (tcpNoDelay) { - s.setTcpNoDelay(); - QPID_LOG(info, - "Set TCP_NODELAY on connection to " << s.getPeerAddress()); - } - - SslAsynchIO *aio; - if (isClient) { - async->setClient(); - aio = - new qpid::sys::windows::ClientSslAsynchIO(brokerHost, - s, - credHandle, - boost::bind(&AsynchIOHandler::readbuff, async, _1, _2), - boost::bind(&AsynchIOHandler::eof, async, _1), - boost::bind(&AsynchIOHandler::disconnect, async, _1), - boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2), - boost::bind(&AsynchIOHandler::nobuffs, async, _1), - boost::bind(&AsynchIOHandler::idle, async, _1)); - } - else { - aio = - new qpid::sys::windows::ServerSslAsynchIO(clientAuthSelected, - s, - credHandle, - boost::bind(&AsynchIOHandler::readbuff, async, _1, _2), - boost::bind(&AsynchIOHandler::eof, async, _1), - boost::bind(&AsynchIOHandler::disconnect, async, _1), - boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2), - boost::bind(&AsynchIOHandler::nobuffs, async, _1), - boost::bind(&AsynchIOHandler::idle, async, _1)); - } - - async->init(aio, brokerTimer, maxNegotiateTime); - aio->start(poller); -} - -uint16_t SslProtocolFactory::getPort() const { - return listeningPort; // Immutable no need for lock. -} - -void SslProtocolFactory::accept(sys::Poller::shared_ptr poller, - sys::ConnectionCodec::Factory* fact) { - for (unsigned i = 0; i<listeners.size(); ++i) { - acceptors.push_back( - AsynchAcceptor::create(listeners[i], - boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false))); - acceptors[i].start(poller); - } -} - -void SslProtocolFactory::connect(sys::Poller::shared_ptr poller, - const std::string& host, - const std::string& port, - sys::ConnectionCodec::Factory* fact, - ConnectFailedCallback failed) -{ - SCHANNEL_CRED cred; - memset(&cred, 0, sizeof(cred)); - cred.dwVersion = SCHANNEL_CRED_VERSION; - SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL, - UNISP_NAME, - SECPKG_CRED_OUTBOUND, - NULL, - &cred, - NULL, - NULL, - &credHandle, - NULL); - if (status != SEC_E_OK) - throw QPID_WINDOWS_ERROR(status); - - brokerHost = host; - // Note that the following logic does not cause a memory leak. - // The allocated Socket is freed either by the AsynchConnector - // upon connection failure or by the AsynchIO upon connection - // shutdown. The allocated AsynchConnector frees itself when it - // is no longer needed. - qpid::sys::Socket* socket = new qpid::sys::Socket(); - connectFailedCallback = failed; - AsynchConnector::create(*socket, - host, - port, - boost::bind(&SslProtocolFactory::established, - this, poller, _1, fact, true), - boost::bind(&SslProtocolFactory::connectFailed, - this, _1, _2, _3)); -} - -namespace -{ -const std::string SSL = "ssl"; -} - -bool SslProtocolFactory::supports(const std::string& capability) -{ - std::string s = capability; - transform(s.begin(), s.end(), s.begin(), tolower); - return s == SSL; -} - -}}} // namespace qpid::sys::windows + if (certStoreLocation == "currentuser") {
+ flags = CERT_SYSTEM_STORE_CURRENT_USER;
+ } else if (certStoreLocation == "localmachine") {
+ flags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ } else if (certStoreLocation == "currentservice") {
+ flags = CERT_SYSTEM_STORE_CURRENT_SERVICE;
+ } else {
+ QPID_LOG(error, "Unrecognised SSL certificate store location: " << options.certStoreLocation
+ << " - Using default location");
+ }
+ HCERTSTORE certStoreHandle;
+ certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
+ X509_ASN_ENCODING,
+ 0,
+ flags |
+ CERT_STORE_READONLY_FLAG,
+ options.certStore.c_str());
+ if (!certStoreHandle)
+ throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+
+ PCCERT_CONTEXT certContext;
+ certContext = ::CertFindCertificateInStore(certStoreHandle,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SUBJECT_STR_A,
+ options.certName.c_str(),
+ NULL);
+ if (certContext == NULL) {
+ int err = ::GetLastError();
+ ::CertCloseStore(certStoreHandle, 0);
+ throw qpid::Exception(QPID_MSG("Locating certificate " << options.certName << " in store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
+ throw QPID_WINDOWS_ERROR(err);
+ }
+
+ SCHANNEL_CRED cred;
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ cred.cCreds = 1;
+ cred.paCred = &certContext;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_INBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ NULL);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+ ::CertFreeCertificateContext(certContext);
+ ::CertCloseStore(certStoreHandle, 0);
+
+ std::vector<std::string> addresses = expandInterfaces(opts.listenInterfaces);
+ if (addresses.empty()) {
+ // We specified some interfaces, but couldn't find addresses for them
+ QPID_LOG(warning, "TCP/TCP6: No specified network interfaces found: Not Listening");
+ listeningPort = 0;
+ }
+
+ for (unsigned i = 0; i<addresses.size(); ++i) {
+ QPID_LOG(debug, "Using interface: " << addresses[i]);
+ SocketAddress sa(addresses[i], boost::lexical_cast<std::string>(options.port));
+
+
+ // We must have at least one resolved address
+ QPID_LOG(info, "SSL Listening to: " << sa.asString())
+ Socket* s = createSocket();
+ listeningPort = s->listen(sa, opts.connectionBacklog);
+ listeners.push_back(s);
+
+ // Try any other resolved addresses
+ while (sa.nextAddress()) {
+ QPID_LOG(info, "SSL Listening to: " << sa.asString())
+ Socket* s = createSocket();
+ s->listen(sa, opts.connectionBacklog);
+ listeners.push_back(s);
+ }
+ }
+}
+
+SslProtocolFactory::~SslProtocolFactory() {
+ ::FreeCredentialsHandle(&credHandle);
+}
+
+void SslProtocolFactory::connectFailed(const qpid::sys::Socket&,
+ int err,
+ const std::string& msg) {
+ if (connectFailedCallback)
+ connectFailedCallback(err, msg);
+}
+
+void SslProtocolFactory::establishedIncoming(sys::Poller::shared_ptr poller,
+ const qpid::sys::Socket& s,
+ sys::ConnectionCodec::Factory* f) {
+ sys::AsynchIOHandler* async = new sys::AsynchIOHandler(s.getFullAddress(), f, false, false);
+
+ sys::AsynchIO *aio =
+ new qpid::sys::windows::ServerSslAsynchIO(
+ clientAuthSelected,
+ s,
+ credHandle,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+
+ establishedCommon(poller, async, aio, s);
+}
+
+void SslProtocolFactory::establishedOutgoing(sys::Poller::shared_ptr poller,
+ const qpid::sys::Socket& s,
+ sys::ConnectionCodec::Factory* f,
+ std::string& name) {
+ sys::AsynchIOHandler* async = new sys::AsynchIOHandler(name, f, true, false);
+
+ sys::AsynchIO *aio =
+ new qpid::sys::windows::ClientSslAsynchIO(
+ brokerHost,
+ s,
+ credHandle,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+
+ establishedCommon(poller, async, aio, s);
+}
+
+void SslProtocolFactory::establishedCommon(sys::Poller::shared_ptr poller,
+ sys::AsynchIOHandler* async,
+ sys::AsynchIO* aio,
+ const qpid::sys::Socket& s) {
+ if (tcpNoDelay) {
+ s.setTcpNoDelay();
+ QPID_LOG(info,
+ "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+ }
+
+ async->init(aio, brokerTimer, maxNegotiateTime);
+ aio->start(poller);
+}
+
+uint16_t SslProtocolFactory::getPort() const {
+ return listeningPort; // Immutable no need for lock.
+}
+
+void SslProtocolFactory::accept(sys::Poller::shared_ptr poller,
+ sys::ConnectionCodec::Factory* fact) {
+ for (unsigned i = 0; i<listeners.size(); ++i) {
+ acceptors.push_back(
+ AsynchAcceptor::create(listeners[i],
+ boost::bind(&SslProtocolFactory::establishedIncoming, this, poller, _1, fact)));
+ acceptors[i].start(poller);
+ }
+}
+
+void SslProtocolFactory::connect(sys::Poller::shared_ptr poller,
+ const std::string& name,
+ const std::string& host,
+ const std::string& port,
+ sys::ConnectionCodec::Factory* fact,
+ ConnectFailedCallback failed)
+{
+ SCHANNEL_CRED cred;
+ memset(&cred, 0, sizeof(cred));
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
+ UNISP_NAME,
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ &cred,
+ NULL,
+ NULL,
+ &credHandle,
+ NULL);
+ if (status != SEC_E_OK)
+ throw QPID_WINDOWS_ERROR(status);
+
+ brokerHost = host;
+ // Note that the following logic does not cause a memory leak.
+ // The allocated Socket is freed either by the AsynchConnector
+ // upon connection failure or by the AsynchIO upon connection
+ // shutdown. The allocated AsynchConnector frees itself when it
+ // is no longer needed.
+ qpid::sys::Socket* socket = createSocket();
+ connectFailedCallback = failed;
+ AsynchConnector::create(*socket,
+ host,
+ port,
+ boost::bind(&SslProtocolFactory::establishedOutgoing,
+ this, poller, _1, fact, name),
+ boost::bind(&SslProtocolFactory::connectFailed,
+ this, _1, _2, _3));
+}
+
+}}} // namespace qpid::sys::windows
|