blob: 26e42e7892592edeac7a331ce431467737f67ae0 [file] [log] [blame]
#ifndef BROKER_HPP
#define BROKER_HPP
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/// @file
///
/// Common code used by different broker examples.
///
/// The examples add functionality as needed, this helps to make it
/// easier to see the important differences between the examples.
#include "proton/event.hpp"
#include "proton/message.hpp"
#include "proton/handler.hpp"
#include "proton/sender.hpp"
#include "proton/transport.hpp"
#include "proton/url.hpp"
#include <iostream>
#include <deque>
#include <map>
#include <list>
#include <sstream>
/// A simple implementation of a queue.
class queue {
public:
queue(const std::string &name, bool dynamic = false) : name_(name), dynamic_(dynamic) {}
std::string name() const { return name_; }
void subscribe(proton::sender s) {
consumers_.push_back(s);
}
// Return true if queue can be deleted.
bool unsubscribe(proton::sender s) {
consumers_.remove(s);
return (consumers_.size() == 0 && (dynamic_ || messages_.size() == 0));
}
void publish(const proton::message &m, proton::receiver r) {
messages_.push_back(m);
dispatch(0);
}
void dispatch(proton::sender *s) {
while (deliver_to(s)) {}
}
bool deliver_to(proton::sender *s) {
// Deliver to single sender if supplied, else all consumers
int count = s ? 1 : consumers_.size();
if (!count) return false;
bool result = false;
sender_list::iterator it = consumers_.begin();
if (!s && count) {
s = &*it;
}
while (messages_.size()) {
if (s->credit()) {
const proton::message& m = messages_.front();
s->send(m);
messages_.pop_front();
result = true;
}
if (--count) {
it++;
} else {
return result;
}
}
return false;
}
private:
typedef std::deque<proton::message> message_queue;
typedef std::list<proton::sender> sender_list;
std::string name_;
bool dynamic_;
message_queue messages_;
sender_list consumers_;
};
/// A collection of queues and queue factory, used by a broker.
class queues {
public:
queues() : next_id_(0) {}
// Get or create a queue.
virtual queue &get(const std::string &address = std::string()) {
if (address.empty()) {
throw std::runtime_error("empty queue name");
}
queue*& q = queues_[address];
if (!q) q = new queue(address);
return *q;
}
// Create a dynamic queue with a unique name.
virtual queue &dynamic() {
std::ostringstream os;
os << "q" << next_id_++;
queue *q = queues_[os.str()] = new queue(os.str(), true);
return *q;
}
// Delete the named queue
virtual void erase(std::string &name) {
delete queues_[name];
queues_.erase(name);
}
protected:
typedef std::map<std::string, queue *> queue_map;
queue_map queues_;
uint64_t next_id_; // Use to generate unique queue IDs.
};
/** Common handler logic for brokers. */
class broker_handler : public proton::handler {
public:
broker_handler(queues& qs) : queues_(qs) {}
void on_link_open(proton::event &e) {
proton::link lnk = e.link();
if (!!lnk.sender()) {
proton::terminus remote_source(lnk.remote_source());
queue &q = remote_source.dynamic() ?
queues_.dynamic() : queues_.get(remote_source.address());
lnk.local_source().address(q.name());
q.subscribe(lnk.sender());
std::cout << "broker outgoing link from " << q.name() << std::endl;
} else {
// Receiver
std::string address = lnk.remote_target().address();
if (!address.empty()) {
lnk.local_target().address(address);
std::cout << "broker incoming link to " << address << std::endl;
}
}
}
void unsubscribe(proton::sender lnk) {
std::string address = lnk.local_source().address();
if (queues_.get(address).unsubscribe(lnk)) {
queues_.erase(address);
}
}
void on_link_close(proton::event &e) {
proton::link lnk = e.link();
if (!!lnk.sender()) {
unsubscribe(lnk.sender());
}
}
void on_connection_close(proton::event &e) {
remove_stale_consumers(e.connection());
}
void on_transport_close(proton::event &e) {
remove_stale_consumers(e.connection());
}
void on_transport_error(proton::event &e) {
std::cout << "broker client disconnect: " << e.transport().condition().str() << std::endl;
}
void on_unhandled_error(proton::event &e, const proton::condition &c) {
std::cerr << "broker error: " << e.name() << ":" << c.str() << std::endl;
}
void remove_stale_consumers(proton::connection connection) {
proton::link_range r = connection.find_links(proton::endpoint::REMOTE_ACTIVE);
for (proton::link_iterator l = r.begin(); l != r.end(); ++l) {
if (!!l->sender()) {
unsubscribe(l->sender());
}
}
}
void on_sendable(proton::event &e) {
proton::link lnk = e.link();
std::string address = lnk.local_source().address();
proton::sender s = lnk.sender();
queues_.get(address).dispatch(&s);
}
void on_message(proton::event &e) {
std::string address = e.link().local_target().address();
queues_.get(address).publish(e.message(), e.link().receiver());
}
protected:
queues& queues_;
};
#endif // BROKER_HPP