diff options
Diffstat (limited to 'cpp/lib')
219 files changed, 19448 insertions, 0 deletions
diff --git a/cpp/lib/Makefile.am b/cpp/lib/Makefile.am new file mode 100644 index 0000000000..09a689bc76 --- /dev/null +++ b/cpp/lib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = common broker client diff --git a/cpp/lib/broker/AccumulatedAck.cpp b/cpp/lib/broker/AccumulatedAck.cpp new file mode 100644 index 0000000000..ff471b0287 --- /dev/null +++ b/cpp/lib/broker/AccumulatedAck.cpp @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "AccumulatedAck.h" + +#include <assert.h> + +using std::less_equal; +using std::bind2nd; +using namespace qpid::broker; + +void AccumulatedAck::update(uint64_t firstTag, uint64_t lastTag){ + assert(firstTag<=lastTag); + if (firstTag <= range + 1) { + if (lastTag > range) range = lastTag; + } else { + for (uint64_t tag = firstTag; tag<=lastTag; tag++) + individual.push_back(tag); + } +} + +void AccumulatedAck::consolidate(){ + individual.sort(); + //remove any individual tags that are covered by range + individual.remove_if(bind2nd(less_equal<uint64_t>(), range)); + //update range if possible (using <= allows for duplicates from overlapping ranges) + while (individual.front() <= range + 1) { + range = individual.front(); + individual.pop_front(); + } +} + +void AccumulatedAck::clear(){ + range = 0; + individual.clear(); +} + +bool AccumulatedAck::covers(uint64_t tag) const{ + return tag <= range || find(individual.begin(), individual.end(), tag) != individual.end(); +} diff --git a/cpp/lib/broker/AccumulatedAck.h b/cpp/lib/broker/AccumulatedAck.h new file mode 100644 index 0000000000..c4a6e3b79b --- /dev/null +++ b/cpp/lib/broker/AccumulatedAck.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _AccumulatedAck_ +#define _AccumulatedAck_ + +#include <algorithm> +#include <functional> +#include <list> + +namespace qpid { + namespace broker { + /** + * Keeps an accumulated record of acked messages (by delivery + * tag). + */ + class AccumulatedAck { + public: + /** + * If not zero, then everything up to this value has been + * acked. + */ + uint64_t range; + /** + * List of individually acked messages that are not + * included in the range marked by 'range'. + */ + std::list<uint64_t> individual; + + AccumulatedAck(uint64_t r) : range(r) {} + void update(uint64_t firstTag, uint64_t lastTag); + void consolidate(); + void clear(); + bool covers(uint64_t tag) const; + }; + } +} + + +#endif diff --git a/cpp/lib/broker/AutoDelete.cpp b/cpp/lib/broker/AutoDelete.cpp new file mode 100644 index 0000000000..2037a9c71c --- /dev/null +++ b/cpp/lib/broker/AutoDelete.cpp @@ -0,0 +1,86 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <AutoDelete.h> +#include <sys/Time.h> + +using namespace qpid::broker; +using namespace qpid::sys; + +AutoDelete::AutoDelete(QueueRegistry* const _registry, uint32_t _period) + : registry(_registry), period(_period), stopped(true) { } + +void AutoDelete::add(Queue::shared_ptr const queue){ + Mutex::ScopedLock l(lock); + queues.push(queue); +} + +Queue::shared_ptr const AutoDelete::pop(){ + Queue::shared_ptr next; + Mutex::ScopedLock l(lock); + if(!queues.empty()){ + next = queues.front(); + queues.pop(); + } + return next; +} + +void AutoDelete::process(){ + Queue::shared_ptr seen; + for(Queue::shared_ptr q = pop(); q; q = pop()){ + if(seen == q){ + add(q); + break; + }else if(q->canAutoDelete()){ + std::string name(q->getName()); + registry->destroy(name); + std::cout << "INFO: Auto-deleted queue named " << name << std::endl; + }else{ + add(q); + if(!seen) seen = q; + } + } +} + +void AutoDelete::run(){ + Monitor::ScopedLock l(monitor); + while(!stopped){ + process(); + monitor.wait(period*TIME_MSEC); + } +} + +void AutoDelete::start(){ + Monitor::ScopedLock l(monitor); + if(stopped){ + stopped = false; + runner = Thread(this); + } +} + +void AutoDelete::stop(){ + { + Monitor::ScopedLock l(monitor); + if(stopped) return; + stopped = true; + } + monitor.notify(); + runner.join(); +} diff --git a/cpp/lib/broker/AutoDelete.h b/cpp/lib/broker/AutoDelete.h new file mode 100644 index 0000000000..9034de1730 --- /dev/null +++ b/cpp/lib/broker/AutoDelete.h @@ -0,0 +1,55 @@ +#ifndef _AutoDelete_ +#define _AutoDelete_ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <queue> +#include <sys/Monitor.h> +#include <BrokerQueue.h> +#include <QueueRegistry.h> +#include <sys/Thread.h> + +namespace qpid { + namespace broker{ + class AutoDelete : private qpid::sys::Runnable { + qpid::sys::Mutex lock; + qpid::sys::Monitor monitor; + std::queue<Queue::shared_ptr> queues; + QueueRegistry* const registry; + uint32_t period; + volatile bool stopped; + qpid::sys::Thread runner; + + Queue::shared_ptr const pop(); + void process(); + virtual void run(); + + public: + AutoDelete(QueueRegistry* const registry, uint32_t period); + void add(Queue::shared_ptr const); + void start(); + void stop(); + }; + } +} + + +#endif diff --git a/cpp/lib/broker/Binding.h b/cpp/lib/broker/Binding.h new file mode 100644 index 0000000000..16ca223208 --- /dev/null +++ b/cpp/lib/broker/Binding.h @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Binding_ +#define _Binding_ + +#include <FieldTable.h> + +namespace qpid { + namespace broker { + class Binding{ + public: + virtual void cancel() = 0; + virtual ~Binding(){} + }; + } +} + + +#endif + diff --git a/cpp/lib/broker/Broker.cpp b/cpp/lib/broker/Broker.cpp new file mode 100644 index 0000000000..f650452e33 --- /dev/null +++ b/cpp/lib/broker/Broker.cpp @@ -0,0 +1,118 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <memory> + +#include "AMQFrame.h" +#include "DirectExchange.h" +#include "TopicExchange.h" +#include "FanOutExchange.h" +#include "HeadersExchange.h" +#include "MessageStoreModule.h" +#include "NullMessageStore.h" +#include "ProtocolInitiation.h" +#include "Connection.h" +#include "sys/ConnectionInputHandler.h" +#include "sys/ConnectionInputHandlerFactory.h" +#include "sys/TimeoutHandler.h" + +#include "Broker.h" + +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"); + +Broker::Broker(const Configuration& conf) : + config(conf), + queues(store.get()), + timeout(30000), + stagingThreshold(0), + cleaner(&queues, timeout/10), + factory(*this) +{ + if (config.getStore().empty()) + store.reset(new NullMessageStore(config.isTrace())); + else + store.reset(new MessageStoreModule(config.getStore())); + + exchanges.declare(empty, DirectExchange::typeName); // Default exchange. + exchanges.declare(amq_direct, DirectExchange::typeName); + exchanges.declare(amq_topic, TopicExchange::typeName); + exchanges.declare(amq_fanout, FanOutExchange::typeName); + exchanges.declare(amq_match, HeadersExchange::typeName); + + if(store.get()) { + RecoveryManager recoverer(queues, exchanges); + MessageStoreSettings storeSettings = { getStagingThreshold() }; + store->recover(recoverer, &storeSettings); + } + + cleaner.start(); +} + + +Broker::shared_ptr Broker::create(int16_t port) +{ + Configuration config; + config.setPort(port); + return create(config); +} + +Broker::shared_ptr Broker::create(const Configuration& config) { + return Broker::shared_ptr(new Broker(config)); +} + +void Broker::run() { + getAcceptor().run(&factory); +} + +void Broker::shutdown() { + if (acceptor) + acceptor->shutdown(); +} + +Broker::~Broker() { + shutdown(); +} + +int16_t Broker::getPort() const { return getAcceptor().getPort(); } + +Acceptor& Broker::getAcceptor() const { + if (!acceptor) + const_cast<Acceptor::shared_ptr&>(acceptor) = + Acceptor::create(config.getPort(), + config.getConnectionBacklog(), + config.getWorkerThreads(), + config.isTrace()); + return *acceptor; +} + + +const int16_t Broker::DEFAULT_PORT(5672); + + +}} // namespace qpid::broker + diff --git a/cpp/lib/broker/Broker.h b/cpp/lib/broker/Broker.h new file mode 100644 index 0000000000..7c21e90b18 --- /dev/null +++ b/cpp/lib/broker/Broker.h @@ -0,0 +1,106 @@ +#ifndef _Broker_ +#define _Broker_ + +/* + * + * 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 <Configuration.h> +#include <ConnectionFactory.h> +#include <sys/Runnable.h> +#include <sys/Acceptor.h> +#include <SharedObject.h> +#include <MessageStore.h> +#include <AutoDelete.h> +#include <ExchangeRegistry.h> +#include <ConnectionToken.h> +#include <DirectExchange.h> +#include <OutputHandler.h> +#include <ProtocolInitiation.h> +#include <QueueRegistry.h> + +namespace qpid { +namespace broker { +/** + * A broker instance. + */ +class Broker : public sys::Runnable, + public SharedObject<Broker> +{ + public: + static const int16_t DEFAULT_PORT; + + virtual ~Broker(); + + /** + * Create a broker. + * @param port Port to listen on or 0 to pick a port dynamically. + */ + static shared_ptr create(int16_t port = DEFAULT_PORT); + + /** + * Create a broker using a Configuration. + */ + static shared_ptr create(const Configuration& config); + + /** + * Return listening port. If called before bind this is + * the configured port. If called after it is the actual + * port, which will be different if the configured port is + * 0. + */ + virtual int16_t getPort() const; + + /** + * Run the broker. Implements Runnable::run() so the broker + * can be run in a separate thread. + */ + virtual void run(); + + /** Shut down the broker */ + virtual void shutdown(); + + MessageStore& getStore() { return *store; } + QueueRegistry& getQueues() { return queues; } + ExchangeRegistry& getExchanges() { return exchanges; } + uint32_t getTimeout() { return timeout; } + uint64_t getStagingThreshold() { return stagingThreshold; } + AutoDelete& getCleaner() { return cleaner; } + + private: + Broker(const Configuration& config); + sys::Acceptor& getAcceptor() const; + + Configuration config; + sys::Acceptor::shared_ptr acceptor; + std::auto_ptr<MessageStore> store; + QueueRegistry queues; + ExchangeRegistry exchanges; + uint32_t timeout; + uint64_t stagingThreshold; + AutoDelete cleaner; + ConnectionFactory factory; +}; + +}} + + + +#endif /*!_Broker_*/ diff --git a/cpp/lib/broker/BrokerAdapter.cpp b/cpp/lib/broker/BrokerAdapter.cpp new file mode 100644 index 0000000000..981801c40e --- /dev/null +++ b/cpp/lib/broker/BrokerAdapter.cpp @@ -0,0 +1,388 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <boost/format.hpp> + +#include "BrokerAdapter.h" +#include "BrokerChannel.h" +#include "Connection.h" +#include "AMQMethodBody.h" +#include "Exception.h" + +namespace qpid { +namespace broker { + +using boost::format; +using namespace qpid; +using namespace qpid::framing; + +typedef std::vector<Queue::shared_ptr> QueueVector; + + +BrokerAdapter::BrokerAdapter(Channel& ch, Connection& c, Broker& b) : + CoreRefs(ch, c, b), + connection(c), + basicHandler(*this), + channelHandler(*this), + connectionHandler(*this), + exchangeHandler(*this), + messageHandler(*this), + queueHandler(*this), + txHandler(*this) +{} + + +ProtocolVersion BrokerAdapter::getVersion() const { + return connection.getVersion(); +} + +void BrokerAdapter::ConnectionHandlerImpl::startOk( + const MethodContext&, const FieldTable& /*clientProperties*/, + const string& /*mechanism*/, + const string& /*response*/, const string& /*locale*/) +{ + client.tune( + 100, connection.getFrameMax(), connection.getHeartbeat()); +} + +void BrokerAdapter::ConnectionHandlerImpl::secureOk( + const MethodContext&, const string& /*response*/){} + +void BrokerAdapter::ConnectionHandlerImpl::tuneOk( + const MethodContext&, uint16_t /*channelmax*/, + uint32_t framemax, uint16_t heartbeat) +{ + connection.setFrameMax(framemax); + connection.setHeartbeat(heartbeat); +} + +void BrokerAdapter::ConnectionHandlerImpl::open( + const MethodContext& context, const string& /*virtualHost*/, + const string& /*capabilities*/, bool /*insist*/) +{ + string knownhosts; + client.openOk( + knownhosts, context.getRequestId()); +} + +void BrokerAdapter::ConnectionHandlerImpl::close( + const MethodContext& context, uint16_t /*replyCode*/, const string& /*replyText*/, + uint16_t /*classId*/, uint16_t /*methodId*/) +{ + client.closeOk(context.getRequestId()); + connection.getOutput().close(); +} + +void BrokerAdapter::ConnectionHandlerImpl::closeOk(const MethodContext&){ + connection.getOutput().close(); +} + +void BrokerAdapter::ChannelHandlerImpl::open( + const MethodContext& context, const string& /*outOfBand*/){ + channel.open(); + // FIXME aconway 2007-01-04: provide valid ID as per ampq 0-9 + client.openOk( + std::string()/* ID */, context.getRequestId()); +} + +void BrokerAdapter::ChannelHandlerImpl::flow(const MethodContext&, bool /*active*/){} +void BrokerAdapter::ChannelHandlerImpl::flowOk(const MethodContext&, bool /*active*/){} + +void BrokerAdapter::ChannelHandlerImpl::close( + const MethodContext& context, uint16_t /*replyCode*/, + const string& /*replyText*/, + uint16_t /*classId*/, uint16_t /*methodId*/) +{ + client.closeOk(context.getRequestId()); + // FIXME aconway 2007-01-18: Following line will "delete this". Ugly. + connection.closeChannel(channel.getId()); +} + +void BrokerAdapter::ChannelHandlerImpl::closeOk(const MethodContext&){} + + + +void BrokerAdapter::ExchangeHandlerImpl::declare(const MethodContext& context, uint16_t /*ticket*/, const string& exchange, const string& type, + bool passive, bool /*durable*/, bool /*autoDelete*/, bool /*internal*/, bool nowait, + const FieldTable& /*arguments*/){ + + if(passive){ + if(!broker.getExchanges().get(exchange)) { + throw ChannelException(404, "Exchange not found: " + exchange); + } + }else{ + try{ + std::pair<Exchange::shared_ptr, bool> response = broker.getExchanges().declare(exchange, type); + if(!response.second && response.first->getType() != type){ + throw ConnectionException( + 530, + "Exchange already declared to be of type " + + response.first->getType() + ", requested " + type); + } + }catch(UnknownExchangeTypeException& e){ + throw ConnectionException( + 503, "Exchange type not implemented: " + type); + } + } + if(!nowait){ + client.declareOk(context.getRequestId()); + } +} + +void BrokerAdapter::ExchangeHandlerImpl::delete_(const MethodContext& context, uint16_t /*ticket*/, + const string& exchange, bool /*ifUnused*/, bool nowait){ + + //TODO: implement unused + broker.getExchanges().destroy(exchange); + if(!nowait) client.deleteOk(context.getRequestId()); +} + +void BrokerAdapter::QueueHandlerImpl::declare(const MethodContext& context, uint16_t /*ticket*/, const string& name, + bool passive, bool durable, bool exclusive, + bool autoDelete, bool nowait, const qpid::framing::FieldTable& arguments){ + Queue::shared_ptr queue; + if (passive && !name.empty()) { + queue = connection.getQueue(name, channel.getId()); + } else { + std::pair<Queue::shared_ptr, bool> queue_created = + broker.getQueues().declare( + name, durable, + autoDelete ? connection.getTimeout() : 0, + exclusive ? &connection : 0); + queue = queue_created.first; + assert(queue); + if (queue_created.second) { // This is a new queue + channel.setDefaultQueue(queue); + + //apply settings & create persistent record if required + queue_created.first->create(arguments); + + //add default binding: + broker.getExchanges().getDefault()->bind(queue, name, 0); + if (exclusive) { + connection.exclusiveQueues.push_back(queue); + } else if(autoDelete){ + broker.getCleaner().add(queue); + } + } + } + if (exclusive && !queue->isExclusiveOwner(&connection)) + throw ChannelException( + 405, + format("Cannot grant exclusive access to queue '%s'") + % queue->getName()); + if (!nowait) { + string queueName = queue->getName(); + client.declareOk( + queueName, queue->getMessageCount(), queue->getConsumerCount(), + context.getRequestId()); + } +} + +void BrokerAdapter::QueueHandlerImpl::bind(const MethodContext& context, uint16_t /*ticket*/, const string& queueName, + const string& exchangeName, const string& routingKey, bool nowait, + const FieldTable& arguments){ + + Queue::shared_ptr queue = connection.getQueue(queueName, channel.getId()); + Exchange::shared_ptr exchange = broker.getExchanges().get(exchangeName); + if(exchange){ + string exchangeRoutingKey = routingKey.empty() && queueName.empty() ? queue->getName() : routingKey; + exchange->bind(queue, exchangeRoutingKey, &arguments); + if(!nowait) client.bindOk(context.getRequestId()); + }else{ + throw ChannelException( + 404, "Bind failed. No such exchange: " + exchangeName); + } +} + +void +BrokerAdapter::QueueHandlerImpl::unbind( + const MethodContext& context, + uint16_t /*ticket*/, + const string& queueName, + const string& exchangeName, + const string& routingKey, + const qpid::framing::FieldTable& arguments ) +{ + Queue::shared_ptr queue = connection.getQueue(queueName, channel.getId()); + if (!queue.get()) throw ChannelException(404, "Unbind failed. No such exchange: " + exchangeName); + + Exchange::shared_ptr exchange = broker.getExchanges().get(exchangeName); + if (!exchange.get()) throw ChannelException(404, "Unbind failed. No such exchange: " + exchangeName); + + exchange->unbind(queue, routingKey, &arguments); + + client.unbindOk(context.getRequestId()); +} + +void BrokerAdapter::QueueHandlerImpl::purge(const MethodContext& context, uint16_t /*ticket*/, const string& queueName, bool nowait){ + + Queue::shared_ptr queue = connection.getQueue(queueName, channel.getId()); + int count = queue->purge(); + if(!nowait) client.purgeOk( count, context.getRequestId()); +} + +void BrokerAdapter::QueueHandlerImpl::delete_(const MethodContext& context, uint16_t /*ticket*/, const string& queue, + bool ifUnused, bool ifEmpty, bool nowait){ + ChannelException error(0, ""); + int count(0); + Queue::shared_ptr q = connection.getQueue(queue, channel.getId()); + if(ifEmpty && q->getMessageCount() > 0){ + throw ChannelException(406, "Queue not empty."); + }else if(ifUnused && q->getConsumerCount() > 0){ + throw ChannelException(406, "Queue in use."); + }else{ + //remove the queue from the list of exclusive queues if necessary + if(q->isExclusiveOwner(&connection)){ + QueueVector::iterator i = find(connection.exclusiveQueues.begin(), connection.exclusiveQueues.end(), q); + if(i < connection.exclusiveQueues.end()) connection.exclusiveQueues.erase(i); + } + count = q->getMessageCount(); + q->destroy(); + broker.getQueues().destroy(queue); + } + + if(!nowait) + client.deleteOk(count, context.getRequestId()); +} + + + + +void BrokerAdapter::BasicHandlerImpl::qos(const MethodContext& context, uint32_t prefetchSize, uint16_t prefetchCount, bool /*global*/){ + //TODO: handle global + channel.setPrefetchSize(prefetchSize); + channel.setPrefetchCount(prefetchCount); + client.qosOk(context.getRequestId()); +} + +void BrokerAdapter::BasicHandlerImpl::consume( + const MethodContext& context, uint16_t /*ticket*/, + const string& queueName, const string& consumerTag, + bool noLocal, bool noAck, bool exclusive, + bool nowait, const FieldTable& fields) +{ + + Queue::shared_ptr queue = connection.getQueue(queueName, channel.getId()); + if(!consumerTag.empty() && channel.exists(consumerTag)){ + throw ConnectionException(530, "Consumer tags must be unique"); + } + + string newTag = consumerTag; + channel.consume( + newTag, queue, !noAck, exclusive, noLocal ? &connection : 0, &fields); + + if(!nowait) client.consumeOk(newTag, context.getRequestId()); + + //allow messages to be dispatched if required as there is now a consumer: + queue->dispatch(); +} + +void BrokerAdapter::BasicHandlerImpl::cancel(const MethodContext& context, const string& consumerTag, bool nowait){ + channel.cancel(consumerTag); + + if(!nowait) client.cancelOk(consumerTag, context.getRequestId()); +} + +void BrokerAdapter::BasicHandlerImpl::publish( + const MethodContext& context, uint16_t /*ticket*/, + const string& exchangeName, const string& routingKey, + bool mandatory, bool immediate) +{ + + Exchange::shared_ptr exchange = exchangeName.empty() ? broker.getExchanges().getDefault() : broker.getExchanges().get(exchangeName); + if(exchange){ + BasicMessage* msg = new BasicMessage( + &connection, exchangeName, routingKey, mandatory, immediate, + context.methodBody); + channel.handlePublish(msg); + }else{ + throw ChannelException( + 404, "Exchange not found '" + exchangeName + "'"); + } +} + +void BrokerAdapter::BasicHandlerImpl::get(const MethodContext& context, uint16_t /*ticket*/, const string& queueName, bool noAck){ + Queue::shared_ptr queue = connection.getQueue(queueName, channel.getId()); + if(!connection.getChannel(channel.getId()).get(queue, "", !noAck)){ + string clusterId;//not used, part of an imatix hack + + client.getEmpty(clusterId, context.getRequestId()); + } +} + +void BrokerAdapter::BasicHandlerImpl::ack(const MethodContext&, uint64_t deliveryTag, bool multiple){ + channel.ack(deliveryTag, multiple); +} + +void BrokerAdapter::BasicHandlerImpl::reject(const MethodContext&, uint64_t /*deliveryTag*/, bool /*requeue*/){} + +void BrokerAdapter::BasicHandlerImpl::recover(const MethodContext&, bool requeue){ + channel.recover(requeue); +} + +void BrokerAdapter::TxHandlerImpl::select(const MethodContext& context){ + channel.begin(); + client.selectOk(context.getRequestId()); +} + +void BrokerAdapter::TxHandlerImpl::commit(const MethodContext& context){ + channel.commit(); + client.commitOk(context.getRequestId()); +} + +void BrokerAdapter::TxHandlerImpl::rollback(const MethodContext& context){ + + channel.rollback(); + client.rollbackOk(context.getRequestId()); + channel.recover(false); +} + +void +BrokerAdapter::ChannelHandlerImpl::ok( const MethodContext& ) +{ + //no specific action required, generic response handling should be sufficient +} + + +// +// Message class method handlers +// +void +BrokerAdapter::ChannelHandlerImpl::ping( const MethodContext& context) +{ + client.ok(context.getRequestId()); + client.pong(); +} + + +void +BrokerAdapter::ChannelHandlerImpl::pong( const MethodContext& context) +{ + client.ok(context.getRequestId()); +} + +void +BrokerAdapter::ChannelHandlerImpl::resume( + const MethodContext&, + const string& /*channel*/ ) +{ + assert(0); // FIXME aconway 2007-01-04: 0-9 feature +} + +}} // namespace qpid::broker + diff --git a/cpp/lib/broker/BrokerAdapter.h b/cpp/lib/broker/BrokerAdapter.h new file mode 100644 index 0000000000..2fafbcc180 --- /dev/null +++ b/cpp/lib/broker/BrokerAdapter.h @@ -0,0 +1,222 @@ +#ifndef _broker_BrokerAdapter_h +#define _broker_BrokerAdapter_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "AMQP_ServerOperations.h" +#include "HandlerImpl.h" +#include "MessageHandlerImpl.h" +#include "Exception.h" + +namespace qpid { +namespace broker { + +class Channel; +class Connection; +class Broker; +class ChannelHandler; +class ConnectionHandler; +class BasicHandler; +class ExchangeHandler; +class QueueHandler; +class TxHandler; +class MessageHandler; +class AccessHandler; +class FileHandler; +class StreamHandler; +class DtxHandler; +class TunnelHandler; +class MessageHandlerImpl; + +/** + * Per-channel protocol adapter. + * + * A container for a collection of AMQP-class adapters that translate + * AMQP method bodies into calls on the core Channel, Connection and + * Broker objects. Each adapter class also provides a client proxy + * to send methods to the peer. + * + */ +class BrokerAdapter : public CoreRefs, public framing::AMQP_ServerOperations +{ + public: + BrokerAdapter(Channel& ch, Connection& c, Broker& b); + + framing::ProtocolVersion getVersion() const; + ChannelHandler* getChannelHandler() { return &channelHandler; } + ConnectionHandler* getConnectionHandler() { return &connectionHandler; } + BasicHandler* getBasicHandler() { return &basicHandler; } + ExchangeHandler* getExchangeHandler() { return &exchangeHandler; } + QueueHandler* getQueueHandler() { return &queueHandler; } + TxHandler* getTxHandler() { return &txHandler; } + MessageHandler* getMessageHandler() { return &messageHandler; } + AccessHandler* getAccessHandler() { + throw ConnectionException(540, "Access class not implemented"); } + FileHandler* getFileHandler() { + throw ConnectionException(540, "File class not implemented"); } + StreamHandler* getStreamHandler() { + throw ConnectionException(540, "Stream class not implemented"); } + DtxHandler* getDtxHandler() { + throw ConnectionException(540, "Dtx class not implemented"); } + TunnelHandler* getTunnelHandler() { + throw ConnectionException(540, "Tunnel class not implemented"); } + + framing::AMQP_ClientProxy& getProxy() { return proxy; } + + private: + + class ConnectionHandlerImpl : + public ConnectionHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Connection> + { + public: + ConnectionHandlerImpl(BrokerAdapter& parent) : HandlerImplType(parent) {} + + void startOk(const framing::MethodContext& context, + const qpid::framing::FieldTable& clientProperties, + const std::string& mechanism, const std::string& response, + const std::string& locale); + void secureOk(const framing::MethodContext& context, + const std::string& response); + void tuneOk(const framing::MethodContext& context, + uint16_t channelMax, + uint32_t frameMax, uint16_t heartbeat); + void open(const framing::MethodContext& context, + const std::string& virtualHost, + const std::string& capabilities, bool insist); + void close(const framing::MethodContext& context, uint16_t replyCode, + const std::string& replyText, + uint16_t classId, uint16_t methodId); + void closeOk(const framing::MethodContext& context); + }; + + class ChannelHandlerImpl : + public ChannelHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Channel> + { + public: + ChannelHandlerImpl(BrokerAdapter& parent) : HandlerImplType(parent) {} + + void open(const framing::MethodContext& context, const std::string& outOfBand); + void flow(const framing::MethodContext& context, bool active); + void flowOk(const framing::MethodContext& context, bool active); + void ok( const framing::MethodContext& context ); + void ping( const framing::MethodContext& context ); + void pong( const framing::MethodContext& context ); + void resume( const framing::MethodContext& context, const std::string& channelId ); + void close(const framing::MethodContext& context, uint16_t replyCode, const + std::string& replyText, uint16_t classId, uint16_t methodId); + void closeOk(const framing::MethodContext& context); + }; + + class ExchangeHandlerImpl : + public ExchangeHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Exchange> + { + public: + ExchangeHandlerImpl(BrokerAdapter& parent) : HandlerImplType(parent) {} + + void declare(const framing::MethodContext& context, uint16_t ticket, + const std::string& exchange, const std::string& type, + bool passive, bool durable, bool autoDelete, + bool internal, bool nowait, + const qpid::framing::FieldTable& arguments); + void delete_(const framing::MethodContext& context, uint16_t ticket, + const std::string& exchange, bool ifUnused, bool nowait); + }; + + class QueueHandlerImpl : + public QueueHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Queue> + { + public: + QueueHandlerImpl(BrokerAdapter& parent) : HandlerImplType(parent) {} + + void declare(const framing::MethodContext& context, uint16_t ticket, const std::string& queue, + bool passive, bool durable, bool exclusive, + bool autoDelete, bool nowait, + const qpid::framing::FieldTable& arguments); + void bind(const framing::MethodContext& context, uint16_t ticket, const std::string& queue, + const std::string& exchange, const std::string& routingKey, + bool nowait, const qpid::framing::FieldTable& arguments); + void unbind(const framing::MethodContext& context, + uint16_t ticket, + const std::string& queue, + const std::string& exchange, + const std::string& routingKey, + const qpid::framing::FieldTable& arguments ); + void purge(const framing::MethodContext& context, uint16_t ticket, const std::string& queue, + bool nowait); + void delete_(const framing::MethodContext& context, uint16_t ticket, const std::string& queue, + bool ifUnused, bool ifEmpty, + bool nowait); + }; + + class BasicHandlerImpl : + public BasicHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Basic> + { + public: + BasicHandlerImpl(BrokerAdapter& parent) : HandlerImplType(parent) {} + + void qos(const framing::MethodContext& context, uint32_t prefetchSize, + uint16_t prefetchCount, bool global); + void consume( + const framing::MethodContext& context, uint16_t ticket, const std::string& queue, + const std::string& consumerTag, bool noLocal, bool noAck, + bool exclusive, bool nowait, + const qpid::framing::FieldTable& fields); + void cancel(const framing::MethodContext& context, const std::string& consumerTag, + bool nowait); + void publish(const framing::MethodContext& context, uint16_t ticket, + const std::string& exchange, const std::string& routingKey, + bool mandatory, bool immediate); + void get(const framing::MethodContext& context, uint16_t ticket, const std::string& queue, + bool noAck); + void ack(const framing::MethodContext& context, uint64_t deliveryTag, bool multiple); + void reject(const framing::MethodContext& context, uint64_t deliveryTag, bool requeue); + void recover(const framing::MethodContext& context, bool requeue); + }; + + class TxHandlerImpl : + public TxHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Tx> + { + public: + TxHandlerImpl(BrokerAdapter& parent) : HandlerImplType(parent) {} + + void select(const framing::MethodContext& context); + void commit(const framing::MethodContext& context); + void rollback(const framing::MethodContext& context); + }; + + Connection& connection; + BasicHandlerImpl basicHandler; + ChannelHandlerImpl channelHandler; + ConnectionHandlerImpl connectionHandler; + ExchangeHandlerImpl exchangeHandler; + MessageHandlerImpl messageHandler; + QueueHandlerImpl queueHandler; + TxHandlerImpl txHandler; + +}; +}} // namespace qpid::broker + + + +#endif /*!_broker_BrokerAdapter_h*/ diff --git a/cpp/lib/broker/BrokerChannel.cpp b/cpp/lib/broker/BrokerChannel.cpp new file mode 100644 index 0000000000..5673a2c42a --- /dev/null +++ b/cpp/lib/broker/BrokerChannel.cpp @@ -0,0 +1,346 @@ +/* + * + * 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 <assert.h> + +#include <iostream> +#include <sstream> +#include <algorithm> +#include <functional> + +#include <boost/bind.hpp> + +#include "BrokerChannel.h" +#include "DeletingTxOp.h" +#include "framing/ChannelAdapter.h" +#include <QpidError.h> +#include <DeliverableMessage.h> +#include <BrokerQueue.h> +#include <BrokerMessage.h> +#include <MessageStore.h> +#include <TxAck.h> +#include <TxPublish.h> +#include "BrokerAdapter.h" +#include "Connection.h" + +using std::mem_fun_ref; +using std::bind2nd; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + + +Channel::Channel( + Connection& con, ChannelId id, + uint32_t _framesize, MessageStore* const _store, + uint64_t _stagingThreshold +) : + ChannelAdapter(id, &con.getOutput(), con.getVersion()), + connection(con), + currentDeliveryTag(1), + transactional(false), + prefetchSize(0), + prefetchCount(0), + framesize(_framesize), + tagGenerator("sgen"), + accumulatedAck(0), + store(_store), + messageBuilder(this, _store, _stagingThreshold), + opened(id == 0),//channel 0 is automatically open, other must be explicitly opened + adapter(new BrokerAdapter(*this, con, con.broker)) +{ + outstanding.reset(); +} + +Channel::~Channel(){ + close(); +} + +bool Channel::exists(const string& consumerTag){ + return consumers.find(consumerTag) != consumers.end(); +} + +// TODO aconway 2007-02-12: Why is connection token passed in instead +// of using the channel's parent connection? +void Channel::consume(string& tagInOut, Queue::shared_ptr queue, bool acks, + bool exclusive, ConnectionToken* const connection, + const FieldTable*) +{ + if(tagInOut.empty()) + tagInOut = tagGenerator.generate(); + std::auto_ptr<ConsumerImpl> c( + new ConsumerImpl(this, tagInOut, queue, connection, acks)); + queue->consume(c.get(), exclusive);//may throw exception + consumers.insert(tagInOut, c.release()); +} + +void Channel::cancel(const string& tag){ + // consumers is a ptr_map so erase will delete the consumer + // which will call cancel. + ConsumerImplMap::iterator i = consumers.find(tag); + if (i != consumers.end()) + consumers.erase(i); +} + +void Channel::close(){ + opened = false; + consumers.clear(); + recover(true); +} + +void Channel::begin(){ + transactional = true; +} + +void Channel::commit(){ + TxAck txAck(accumulatedAck, unacked); + txBuffer.enlist(&txAck); + if(txBuffer.prepare(store)){ + txBuffer.commit(); + } + accumulatedAck.clear(); +} + +void Channel::rollback(){ + txBuffer.rollback(); + accumulatedAck.clear(); +} + +void Channel::deliver( + Message::shared_ptr& msg, const string& consumerTag, + Queue::shared_ptr& queue, bool ackExpected) +{ + Mutex::ScopedLock locker(deliveryLock); + + // Key the delivered messages to the id of the request in which they're sent + uint64_t deliveryTag = getNextSendRequestId(); + + if(ackExpected){ + unacked.push_back(DeliveryRecord(msg, queue, consumerTag, deliveryTag)); + outstanding.size += msg->contentSize(); + outstanding.count++; + } + //send deliver method, header and content(s) + msg->deliver(*this, consumerTag, deliveryTag, framesize); +} + +bool Channel::checkPrefetch(Message::shared_ptr& msg){ + Mutex::ScopedLock locker(deliveryLock); + bool countOk = !prefetchCount || prefetchCount > unacked.size(); + bool sizeOk = !prefetchSize || prefetchSize > msg->contentSize() + outstanding.size || unacked.empty(); + return countOk && sizeOk; +} + +Channel::ConsumerImpl::ConsumerImpl(Channel* _parent, const string& _tag, + Queue::shared_ptr _queue, + ConnectionToken* const _connection, bool ack +) : parent(_parent), tag(_tag), queue(_queue), connection(_connection), + ackExpected(ack), blocked(false) {} + +bool Channel::ConsumerImpl::deliver(Message::shared_ptr& msg){ + if(!connection || connection != msg->getPublisher()){//check for no_local + if(ackExpected && !parent->checkPrefetch(msg)){ + blocked = true; + }else{ + blocked = false; + parent->deliver(msg, tag, queue, ackExpected); + return true; + } + } + return false; +} + +Channel::ConsumerImpl::~ConsumerImpl() { + cancel(); +} + +void Channel::ConsumerImpl::cancel(){ + if(queue) + queue->cancel(this); +} + +void Channel::ConsumerImpl::requestDispatch(){ + if(blocked) + queue->dispatch(); +} + +void Channel::handleInlineTransfer(Message::shared_ptr msg) +{ + Exchange::shared_ptr exchange = + connection.broker.getExchanges().get(msg->getExchange()); + if(transactional){ + TxPublish* deliverable = new TxPublish(msg); + exchange->route( + *deliverable, msg->getRoutingKey(), + &(msg->getApplicationHeaders())); + txBuffer.enlist(new DeletingTxOp(deliverable)); + }else{ + DeliverableMessage deliverable(msg); + exchange->route( + deliverable, msg->getRoutingKey(), + &(msg->getApplicationHeaders())); + } +} + +void Channel::handlePublish(Message* _message){ + Message::shared_ptr message(_message); + messageBuilder.initialise(message); +} + +void Channel::handleHeader(AMQHeaderBody::shared_ptr header){ + messageBuilder.setHeader(header); + //at this point, decide based on the size of the message whether we want + //to stage it by saving content directly to disk as it arrives +} + +void Channel::handleContent(AMQContentBody::shared_ptr content){ + messageBuilder.addContent(content); +} + +void Channel::handleHeartbeat(boost::shared_ptr<AMQHeartbeatBody>) { + // TODO aconway 2007-01-17: Implement heartbeating. +} + +void Channel::complete(Message::shared_ptr msg) { + Exchange::shared_ptr exchange = + connection.broker.getExchanges().get(msg->getExchange()); + assert(exchange.get()); + if(transactional) { + std::auto_ptr<TxPublish> deliverable(new TxPublish(msg)); + exchange->route(*deliverable, msg->getRoutingKey(), + &(msg->getApplicationHeaders())); + txBuffer.enlist(new DeletingTxOp(deliverable.release())); + } else { + DeliverableMessage deliverable(msg); + exchange->route(deliverable, msg->getRoutingKey(), + &(msg->getApplicationHeaders())); + } +} + +void Channel::ack(){ + ack(getFirstAckRequest(), getLastAckRequest()); +} + +// Used by Basic +void Channel::ack(uint64_t deliveryTag, bool multiple){ + if (multiple) + ack(0, deliveryTag); + else + ack(deliveryTag, deliveryTag); +} + +void Channel::ack(uint64_t firstTag, uint64_t lastTag){ + if(transactional){ + accumulatedAck.update(firstTag, lastTag); + + //TODO: I think the outstanding prefetch size & count should be updated at this point... + //TODO: ...this may then necessitate dispatching to consumers + }else{ + Mutex::ScopedLock locker(deliveryLock);//need to synchronize with possible concurrent delivery + + ack_iterator i = find_if(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::matches), lastTag)); + ack_iterator j = (firstTag == 0) ? + unacked.begin() : + find_if(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::matches), firstTag)); + + if(i == unacked.end()){ + throw ConnectionException(530, "Received ack for unrecognised delivery tag"); + }else if(i!=j){ + ack_iterator end = ++i; + for_each(j, end, mem_fun_ref(&DeliveryRecord::discard)); + unacked.erase(unacked.begin(), end); + + //recalculate the prefetch: + outstanding.reset(); + for_each(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::addTo), &outstanding)); + }else{ + i->discard(); + i->subtractFrom(&outstanding); + unacked.erase(i); + } + + //if the prefetch limit had previously been reached, there may + //be messages that can be now be delivered + std::for_each(consumers.begin(), consumers.end(), + boost::bind(&ConsumerImpl::requestDispatch, _1)); + } +} + +void Channel::recover(bool requeue){ + Mutex::ScopedLock locker(deliveryLock);//need to synchronize with possible concurrent delivery + + if(requeue){ + outstanding.reset(); + std::list<DeliveryRecord> copy = unacked; + unacked.clear(); + for_each(copy.begin(), copy.end(), mem_fun_ref(&DeliveryRecord::requeue)); + }else{ + for_each(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::redeliver), this)); + } +} + +bool Channel::get(Queue::shared_ptr queue, const string& destination, bool ackExpected){ + Message::shared_ptr msg = queue->dequeue(); + if(msg){ + Mutex::ScopedLock locker(deliveryLock); + uint64_t myDeliveryTag = getNextSendRequestId(); + msg->sendGetOk(MethodContext(this, msg->getRespondTo()), + destination, + queue->getMessageCount() + 1, myDeliveryTag, + framesize); + if(ackExpected){ + unacked.push_back(DeliveryRecord(msg, queue, myDeliveryTag)); + } + return true; + }else{ + return false; + } +} + +void Channel::deliver(Message::shared_ptr& msg, const string& consumerTag, + uint64_t deliveryTag) +{ + msg->deliver(*this, consumerTag, deliveryTag, framesize); +} + +void Channel::handleMethodInContext( + boost::shared_ptr<qpid::framing::AMQMethodBody> method, + const MethodContext& context +) +{ + try{ + if(getId() != 0 && !method->isA<ChannelOpenBody>() && !isOpen()) { + std::stringstream out; + out << "Attempt to use unopened channel: " << getId(); + throw ConnectionException(504, out.str()); + } else { + method->invoke(*adapter, context); + } + }catch(ChannelException& e){ + adapter->getProxy().getChannel().close( + e.code, e.toString(), + method->amqpClassId(), method->amqpMethodId()); + connection.closeChannel(getId()); + }catch(ConnectionException& e){ + connection.close(e.code, e.toString(), method->amqpClassId(), method->amqpMethodId()); + }catch(std::exception& e){ + connection.close(541/*internal error*/, e.what(), method->amqpClassId(), method->amqpMethodId()); + } +} diff --git a/cpp/lib/broker/BrokerChannel.h b/cpp/lib/broker/BrokerChannel.h new file mode 100644 index 0000000000..5085783685 --- /dev/null +++ b/cpp/lib/broker/BrokerChannel.h @@ -0,0 +1,159 @@ +#ifndef _broker_BrokerChannel_h +#define _broker_BrokerChannel_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 <list> + +#include <boost/scoped_ptr.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/ptr_container/ptr_map.hpp> + +#include <AccumulatedAck.h> +#include <Consumer.h> +#include <DeliveryRecord.h> +#include <MessageBuilder.h> +#include <NameGenerator.h> +#include <Prefetch.h> +#include <TxBuffer.h> +#include "framing/ChannelAdapter.h" +#include "ChannelOpenBody.h" +#include "CompletionHandler.h" + +namespace qpid { +namespace broker { + +class ConnectionToken; +class Connection; +class Queue; +class BrokerAdapter; + +using framing::string; + +/** + * Maintains state for an AMQP channel. Handles incoming and + * outgoing messages for that channel. + */ +class Channel : public framing::ChannelAdapter, + public CompletionHandler +{ + class ConsumerImpl : public Consumer + { + Channel* parent; + const string tag; + Queue::shared_ptr queue; + ConnectionToken* const connection; + const bool ackExpected; + bool blocked; + + public: + ConsumerImpl(Channel* parent, const string& tag, + Queue::shared_ptr queue, + ConnectionToken* const connection, bool ack); + ~ConsumerImpl(); + virtual bool deliver(Message::shared_ptr& msg); + void cancel(); + void requestDispatch(); + }; + + typedef boost::ptr_map<string,ConsumerImpl> ConsumerImplMap; + + Connection& connection; + uint64_t currentDeliveryTag; + Queue::shared_ptr defaultQueue; + bool transactional; + ConsumerImplMap consumers; + uint32_t prefetchSize; + uint16_t prefetchCount; + Prefetch outstanding; + uint32_t framesize; + NameGenerator tagGenerator; + std::list<DeliveryRecord> unacked; + sys::Mutex deliveryLock; + TxBuffer txBuffer; + AccumulatedAck accumulatedAck; + MessageStore* const store; + MessageBuilder messageBuilder;//builder for in-progress message + bool opened; + boost::scoped_ptr<BrokerAdapter> adapter; + + // completion handler for MessageBuilder + void complete(Message::shared_ptr msg); + + void deliver(Message::shared_ptr& msg, const string& tag, + Queue::shared_ptr& queue, bool ackExpected); + bool checkPrefetch(Message::shared_ptr& msg); + + public: + Channel(Connection& parent, + framing::ChannelId id, + uint32_t framesize, + MessageStore* const _store = 0, + uint64_t stagingThreshold = 0); + + ~Channel(); + + bool isOpen() const { return opened; } + BrokerAdapter& getAdatper() { return *adapter; } + + void open() { opened = true; } + void setDefaultQueue(Queue::shared_ptr queue){ defaultQueue = queue; } + Queue::shared_ptr getDefaultQueue() const { return defaultQueue; } + uint32_t setPrefetchSize(uint32_t size){ return prefetchSize = size; } + uint16_t setPrefetchCount(uint16_t n){ return prefetchCount = n; } + + bool exists(const string& consumerTag); + + /** + *@param tagInOut - if empty it is updated with the generated token. + */ + void consume(string& tagInOut, Queue::shared_ptr queue, bool acks, + bool exclusive, ConnectionToken* const connection = 0, + const framing::FieldTable* = 0); + void cancel(const string& tag); + bool get(Queue::shared_ptr queue, const std::string& destination, bool ackExpected); + void begin(); + void close(); + void commit(); + void rollback(); + void ack(); + void ack(uint64_t deliveryTag, bool multiple); + void ack(uint64_t deliveryTag, uint64_t endTag); + void recover(bool requeue); + void deliver(Message::shared_ptr& msg, const string& consumerTag, uint64_t deliveryTag); + void handlePublish(Message* msg); + void handleHeader(boost::shared_ptr<framing::AMQHeaderBody>); + void handleContent(boost::shared_ptr<framing::AMQContentBody>); + void handleHeartbeat(boost::shared_ptr<framing::AMQHeartbeatBody>); + + void handleInlineTransfer(Message::shared_ptr msg); + + // For ChannelAdapter + void handleMethodInContext( + boost::shared_ptr<framing::AMQMethodBody> method, + const framing::MethodContext& context); +}; + +}} // namespace broker + + +#endif /*!_broker_BrokerChannel_h*/ diff --git a/cpp/lib/broker/BrokerExchange.h b/cpp/lib/broker/BrokerExchange.h new file mode 100644 index 0000000000..6f4e9e6671 --- /dev/null +++ b/cpp/lib/broker/BrokerExchange.h @@ -0,0 +1,51 @@ +#ifndef _broker_BrokerExchange_h +#define _broker_BrokerExchange_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include <Deliverable.h> +#include <BrokerQueue.h> +#include <FieldTable.h> + +namespace qpid { + namespace broker { + using std::string; + + class Exchange{ + const string name; + public: + typedef boost::shared_ptr<Exchange> shared_ptr; + + explicit Exchange(const string& _name) : name(_name){} + virtual ~Exchange(){} + string getName() { return name; } + virtual string getType() = 0; + virtual void bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args) = 0; + }; + } +} + + +#endif /*!_broker_BrokerExchange_h*/ diff --git a/cpp/lib/broker/BrokerMessage.cpp b/cpp/lib/broker/BrokerMessage.cpp new file mode 100644 index 0000000000..91ba3dfec0 --- /dev/null +++ b/cpp/lib/broker/BrokerMessage.cpp @@ -0,0 +1,245 @@ +/* + * + * 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/cast.hpp> + +#include <BrokerMessage.h> +#include <iostream> + +#include <InMemoryContent.h> +#include <LazyLoadedContent.h> +#include <MessageStore.h> +#include <BasicDeliverBody.h> +#include <BasicGetOkBody.h> +#include <AMQContentBody.h> +#include <AMQHeaderBody.h> +#include "AMQMethodBody.h" +#include "AMQFrame.h" +#include "framing/ChannelAdapter.h" + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +BasicMessage::BasicMessage( + const ConnectionToken* const _publisher, + const string& _exchange, const string& _routingKey, + bool _mandatory, bool _immediate, framing::AMQMethodBody::shared_ptr respondTo +) : + Message(_publisher, _exchange, _routingKey, _mandatory, + _immediate, respondTo), + size(0) +{} + +// FIXME aconway 2007-02-01: remove. +// BasicMessage::BasicMessage(Buffer& buffer, bool headersOnly, uint32_t contentChunkSize) : +// publisher(0), size(0) +// { + +// decode(buffer, headersOnly, contentChunkSize); +// } + +// For tests only. +BasicMessage::BasicMessage() : size(0) +{} + +BasicMessage::~BasicMessage(){} + +void BasicMessage::setHeader(AMQHeaderBody::shared_ptr _header){ + this->header = _header; +} + +void BasicMessage::addContent(AMQContentBody::shared_ptr data){ + if (!content.get()) { + content = std::auto_ptr<Content>(new InMemoryContent()); + } + content->add(data); + size += data->size(); +} + +bool BasicMessage::isComplete(){ + return header.get() && (header->getContentSize() == contentSize()); +} + +void BasicMessage::deliver(ChannelAdapter& channel, + const string& consumerTag, uint64_t deliveryTag, + uint32_t framesize) +{ + // CCT -- TODO - Update code generator to take pointer/ not + // instance to avoid extra contruction + channel.send( + new BasicDeliverBody( + channel.getVersion(), consumerTag, deliveryTag, + getRedelivered(), getExchange(), getRoutingKey())); + sendContent(channel, framesize); +} + +void BasicMessage::sendGetOk(const MethodContext& context, + const std::string& /*destination*/, + uint32_t messageCount, + uint64_t deliveryTag, + uint32_t framesize) +{ + // CCT -- TODO - Update code generator to take pointer/ not + // instance to avoid extra contruction + context.channel->send( + new BasicGetOkBody( + context.channel->getVersion(), + context.methodBody->getRequestId(), + deliveryTag, getRedelivered(), getExchange(), + getRoutingKey(), messageCount)); + sendContent(*context.channel, framesize); +} + +void BasicMessage::sendContent( + ChannelAdapter& channel, uint32_t framesize) +{ + channel.send(header); + Mutex::ScopedLock locker(contentLock); + if (content.get()) + content->send(channel, framesize); +} + +BasicHeaderProperties* BasicMessage::getHeaderProperties(){ + return boost::polymorphic_downcast<BasicHeaderProperties*>( + header->getProperties()); +} + +const FieldTable& BasicMessage::getApplicationHeaders(){ + return getHeaderProperties()->getHeaders(); +} + +bool BasicMessage::isPersistent() +{ + if(!header) return false; + BasicHeaderProperties* props = getHeaderProperties(); + return props && props->getDeliveryMode() == PERSISTENT; +} + +void BasicMessage::decode(Buffer& buffer, bool headersOnly, uint32_t contentChunkSize) +{ + decodeHeader(buffer); + if (!headersOnly) decodeContent(buffer, contentChunkSize); +} + +void BasicMessage::decodeHeader(Buffer& buffer) +{ + string exchange; + string routingKey; + + buffer.getShortString(exchange); + buffer.getShortString(routingKey); + setRouting(exchange, routingKey); + + uint32_t headerSize = buffer.getLong(); + AMQHeaderBody::shared_ptr headerBody(new AMQHeaderBody()); + headerBody->decode(buffer, headerSize); + setHeader(headerBody); +} + +void BasicMessage::decodeContent(Buffer& buffer, uint32_t chunkSize) +{ + uint64_t expected = expectedContentSize(); + if (expected != buffer.available()) { + std::cout << "WARN: Expected " << expectedContentSize() << " bytes, got " << buffer.available() << std::endl; + throw Exception("Cannot decode content, buffer not large enough."); + } + + if (!chunkSize || chunkSize > expected) { + chunkSize = expected; + } + + uint64_t total = 0; + while (total < expectedContentSize()) { + uint64_t remaining = expected - total; + AMQContentBody::shared_ptr contentBody(new AMQContentBody()); + contentBody->decode(buffer, remaining < chunkSize ? remaining : chunkSize); + addContent(contentBody); + total += chunkSize; + } +} + +void BasicMessage::encode(Buffer& buffer) +{ + encodeHeader(buffer); + encodeContent(buffer); +} + +void BasicMessage::encodeHeader(Buffer& buffer) +{ + buffer.putShortString(getExchange()); + buffer.putShortString(getRoutingKey()); + buffer.putLong(header->size()); + header->encode(buffer); +} + +void BasicMessage::encodeContent(Buffer& buffer) +{ + Mutex::ScopedLock locker(contentLock); + if (content.get()) content->encode(buffer); +} + +uint32_t BasicMessage::encodedSize() +{ + return encodedHeaderSize() + encodedContentSize(); +} + +uint32_t BasicMessage::encodedContentSize() +{ + Mutex::ScopedLock locker(contentLock); + return content.get() ? content->size() : 0; +} + +uint32_t BasicMessage::encodedHeaderSize() +{ + return getExchange().size() + 1 + + getRoutingKey().size() + 1 + + header->size() + 4;//4 extra bytes for size +} + +uint64_t BasicMessage::expectedContentSize() +{ + return header.get() ? header->getContentSize() : 0; +} + +void BasicMessage::releaseContent(MessageStore* store) +{ + Mutex::ScopedLock locker(contentLock); + if (!isPersistent() && getPersistenceId() == 0) { + store->stage(this); + } + if (!content.get() || content->size() > 0) { + // FIXME aconway 2007-02-07: handle MessageMessage. + //set content to lazy loading mode (but only if there is stored content): + + //Note: the LazyLoadedContent instance contains a raw pointer to the message, however it is + // then set as a member of that message so its lifetime is guaranteed to be no longer than + // that of the message itself + content = std::auto_ptr<Content>( + new LazyLoadedContent(store, this, expectedContentSize())); + } +} + +void BasicMessage::setContent(std::auto_ptr<Content>& _content) +{ + Mutex::ScopedLock locker(contentLock); + content = _content; +} diff --git a/cpp/lib/broker/BrokerMessage.h b/cpp/lib/broker/BrokerMessage.h new file mode 100644 index 0000000000..fcb104edbb --- /dev/null +++ b/cpp/lib/broker/BrokerMessage.h @@ -0,0 +1,137 @@ +#ifndef _broker_BrokerMessage_h +#define _broker_BrokerMessage_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 <memory> +#include <boost/shared_ptr.hpp> + +#include <BrokerMessageBase.h> +#include <BasicHeaderProperties.h> +#include <ConnectionToken.h> +#include <Content.h> +#include <Mutex.h> +#include <TxBuffer.h> + +namespace qpid { + +namespace framing { +class MethodContext; +class ChannelAdapter; +class AMQHeaderBody; +} + +namespace broker { + +class MessageStore; +using framing::string; + +/** + * Represents an AMQP message, i.e. a header body, a list of + * content bodies and some details about the publication + * request. + */ +class BasicMessage : public Message { + boost::shared_ptr<framing::AMQHeaderBody> header; + std::auto_ptr<Content> content; + sys::Mutex contentLock; + uint64_t size; + + void sendContent(framing::ChannelAdapter&, uint32_t framesize); + + public: + typedef boost::shared_ptr<BasicMessage> shared_ptr; + + BasicMessage(const ConnectionToken* const publisher, + const string& exchange, const string& routingKey, + bool mandatory, bool immediate, + boost::shared_ptr<framing::AMQMethodBody> respondTo); + BasicMessage(); + ~BasicMessage(); + void setHeader(boost::shared_ptr<framing::AMQHeaderBody> header); + void addContent(framing::AMQContentBody::shared_ptr data); + bool isComplete(); + + void deliver(framing::ChannelAdapter&, + const string& consumerTag, + uint64_t deliveryTag, + uint32_t framesize); + + void sendGetOk(const framing::MethodContext&, + const std::string& destination, + uint32_t messageCount, + uint64_t deliveryTag, + uint32_t framesize); + + framing::BasicHeaderProperties* getHeaderProperties(); + const framing::FieldTable& getApplicationHeaders(); + bool isPersistent(); + uint64_t contentSize() const { return size; } + + void decode(framing::Buffer& buffer, bool headersOnly = false, + uint32_t contentChunkSize = 0); + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer, uint32_t contentChunkSize = 0); + + void encode(framing::Buffer& buffer); + void encodeHeader(framing::Buffer& buffer); + void encodeContent(framing::Buffer& buffer); + /** + * @returns the size of the buffer needed to encode this + * message in its entirety + */ + uint32_t encodedSize(); + /** + * @returns the size of the buffer needed to encode the + * 'header' of this message (not just the header frame, + * but other meta data e.g.routing key and exchange) + */ + uint32_t encodedHeaderSize(); + /** + * @returns the size of the buffer needed to encode the + * (possibly partial) content held by this message + */ + uint32_t encodedContentSize(); + /** + * Releases the in-memory content data held by this + * message. Must pass in a store from which the data can + * be reloaded. + */ + void releaseContent(MessageStore* store); + /** + * If headers have been received, returns the expected + * content size else returns 0. + */ + uint64_t expectedContentSize(); + /** + * Sets the 'content' implementation of this message (the + * message controls the lifecycle of the content instance + * it uses). + */ + void setContent(std::auto_ptr<Content>& content); +}; + +} +} + + +#endif /*!_broker_BrokerMessage_h*/ diff --git a/cpp/lib/broker/BrokerMessageBase.h b/cpp/lib/broker/BrokerMessageBase.h new file mode 100644 index 0000000000..709369ae2f --- /dev/null +++ b/cpp/lib/broker/BrokerMessageBase.h @@ -0,0 +1,184 @@ +#ifndef _broker_BrokerMessageBase_h +#define _broker_BrokerMessageBase_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 <string> +#include <boost/shared_ptr.hpp> +#include "Content.h" +#include "framing/amqp_types.h" + +namespace qpid { + +namespace framing { +class MethodContext; +class ChannelAdapter; +class BasicHeaderProperties; +class FieldTable; +class AMQMethodBody; +class AMQContentBody; +class AMQHeaderBody; +} + + +namespace broker { +class ConnectionToken; +class MessageStore; + +/** + * Base class for all types of internal broker messages + * abstracting away the operations + * TODO; AMS: for the moment this is mostly a placeholder + */ +class Message { + public: + typedef boost::shared_ptr<Message> shared_ptr; + typedef boost::shared_ptr<framing::AMQMethodBody> AMQMethodBodyPtr; + + + Message(const ConnectionToken* publisher_, + const std::string& _exchange, + const std::string& _routingKey, + bool _mandatory, bool _immediate, + AMQMethodBodyPtr respondTo_) : + publisher(publisher_), + exchange(_exchange), + routingKey(_routingKey), + mandatory(_mandatory), + immediate(_immediate), + persistenceId(0), + redelivered(false), + respondTo(respondTo_) + {} + + Message() : + mandatory(false), + immediate(false), + persistenceId(0), + redelivered(false) + {} + + virtual ~Message() {}; + + // Accessors + const std::string& getRoutingKey() const { return routingKey; } + const std::string& getExchange() const { return exchange; } + uint64_t getPersistenceId() const { return persistenceId; } + bool getRedelivered() const { return redelivered; } + AMQMethodBodyPtr getRespondTo() const { return respondTo; } + + void setRouting(const std::string& _exchange, const std::string& _routingKey) + { exchange = _exchange; routingKey = _routingKey; } + void setPersistenceId(uint64_t _persistenceId) { persistenceId = _persistenceId; } // XXXX: Only used in tests? + void redeliver() { redelivered = true; } + + /** + * Used to deliver the message from the queue + */ + virtual void deliver(framing::ChannelAdapter& channel, + const std::string& consumerTag, + uint64_t deliveryTag, + uint32_t framesize) = 0; + /** + * Used to return a message in response to a get from a queue + */ + virtual void sendGetOk(const framing::MethodContext& context, + const std::string& destination, + uint32_t messageCount, + uint64_t deliveryTag, + uint32_t framesize) = 0; + + virtual bool isComplete() = 0; + + virtual uint64_t contentSize() const = 0; + // FIXME aconway 2007-02-06: Get rid of BasicHeaderProperties + // at this level. Expose only generic properties available from both + // message types (e.g. getApplicationHeaders below). + // + virtual framing::BasicHeaderProperties* getHeaderProperties() = 0; + virtual const framing::FieldTable& getApplicationHeaders() = 0; + virtual bool isPersistent() = 0; + virtual const ConnectionToken* getPublisher() const { + return publisher; + } + + virtual void encode(framing::Buffer& /*buffer*/) {}; // XXXX: Only used in tests? + virtual void encodeHeader(framing::Buffer& /*buffer*/) {}; // XXXX: Only used in tests? + + /** + * @returns the size of the buffer needed to encode this + * message in its entirety + * + * XXXX: Only used in tests? + */ + virtual uint32_t encodedSize() = 0; + /** + * @returns the size of the buffer needed to encode the + * 'header' of this message (not just the header frame, + * but other meta data e.g.routing key and exchange) + * + * XXXX: Only used in tests? + */ + virtual uint32_t encodedHeaderSize() = 0; + /** + * @returns the size of the buffer needed to encode the + * (possibly partial) content held by this message + */ + virtual uint32_t encodedContentSize() = 0; + /** + * If headers have been received, returns the expected + * content size else returns 0. + */ + virtual uint64_t expectedContentSize() = 0; + + // TODO: AMS 29/1/2007 Don't think these are really part of base class + + /** + * Sets the 'content' implementation of this message (the + * message controls the lifecycle of the content instance + * it uses). + */ + virtual void setContent(std::auto_ptr<Content>& /*content*/) {}; + virtual void setHeader(boost::shared_ptr<framing::AMQHeaderBody>) {}; + virtual void addContent(boost::shared_ptr<framing::AMQContentBody>) {}; + /** + * Releases the in-memory content data held by this + * message. Must pass in a store from which the data can + * be reloaded. + */ + virtual void releaseContent(MessageStore* /*store*/) {}; + + private: + const ConnectionToken* publisher; + std::string exchange; + std::string routingKey; + const bool mandatory; + const bool immediate; + uint64_t persistenceId; + bool redelivered; + AMQMethodBodyPtr respondTo; +}; + +}} + + +#endif /*!_broker_BrokerMessage_h*/ diff --git a/cpp/lib/broker/BrokerMessageMessage.cpp b/cpp/lib/broker/BrokerMessageMessage.cpp new file mode 100644 index 0000000000..3449078d70 --- /dev/null +++ b/cpp/lib/broker/BrokerMessageMessage.cpp @@ -0,0 +1,239 @@ +/* + * + * 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 "QpidError.h" +#include "BrokerMessageMessage.h" +#include "ChannelAdapter.h" +#include "MessageTransferBody.h" +#include "MessageOpenBody.h" +#include "MessageCloseBody.h" +#include "MessageAppendBody.h" +#include "Reference.h" +#include "framing/FieldTable.h" +#include "framing/BasicHeaderProperties.h" + +#include <algorithm> + +using namespace std; +using namespace qpid::framing; + +namespace qpid { +namespace broker { + +MessageMessage::MessageMessage( + ConnectionToken* publisher, RequestId requestId_, TransferPtr transfer_ +) : Message(publisher, transfer_->getDestination(), + transfer_->getRoutingKey(), + transfer_->getMandatory(), + transfer_->getImmediate(), + transfer_), + requestId(requestId_), + transfer(transfer_) +{} + +MessageMessage::MessageMessage( + ConnectionToken* publisher, RequestId requestId_, TransferPtr transfer_, + ReferencePtr reference_ +) : Message(publisher, transfer_->getDestination(), + transfer_->getRoutingKey(), + transfer_->getMandatory(), + transfer_->getImmediate(), + transfer_), + requestId(requestId_), + transfer(transfer_), + reference(reference_) +{} + +// TODO: astitcher 1-Mar-2007: This code desperately needs better factoring +void MessageMessage::transferMessage( + framing::ChannelAdapter& channel, + const std::string& consumerTag, + uint32_t framesize) +{ + const framing::Content& body = transfer->getBody(); + + // Send any reference data + if (!body.isInline()){ + // Open + channel.send(new MessageOpenBody(channel.getVersion(), reference->getId())); + // Appends + for(Reference::Appends::const_iterator a = reference->getAppends().begin(); + a != reference->getAppends().end(); + ++a) { + uint32_t sizeleft = (*a)->size(); + const string& content = (*a)->getBytes(); + // Calculate overhead bytes + // Assume that the overhead is constant as the reference name doesn't change + uint32_t overhead = sizeleft - content.size(); + string::size_type contentStart = 0; + while (sizeleft) { + string::size_type contentSize = sizeleft <= framesize ? sizeleft : framesize-overhead; + channel.send(new MessageAppendBody(channel.getVersion(), reference->getId(), + string(content, contentStart, contentSize))); + sizeleft -= contentSize; + contentStart += contentSize; + } + } + } + + // The transfer + if ( transfer->size()<=framesize ) { + channel.send( + new MessageTransferBody(channel.getVersion(), + transfer->getTicket(), + consumerTag, + getRedelivered(), + transfer->getImmediate(), + transfer->getTtl(), + transfer->getPriority(), + transfer->getTimestamp(), + transfer->getDeliveryMode(), + transfer->getExpiration(), + getExchange(), + getRoutingKey(), + transfer->getMessageId(), + transfer->getCorrelationId(), + transfer->getReplyTo(), + transfer->getContentType(), + transfer->getContentEncoding(), + transfer->getUserId(), + transfer->getAppId(), + transfer->getTransactionId(), + transfer->getSecurityToken(), + transfer->getApplicationHeaders(), + body, + transfer->getMandatory())); + } else { + // Thing to do here is to construct a simple reference message then deliver that instead + // fragmentation will be taken care of in the delivery if necessary; + string content = body.getValue(); + string refname = "dummy"; + TransferPtr newTransfer( + new MessageTransferBody(channel.getVersion(), + transfer->getTicket(), + consumerTag, + getRedelivered(), + transfer->getImmediate(), + transfer->getTtl(), + transfer->getPriority(), + transfer->getTimestamp(), + transfer->getDeliveryMode(), + transfer->getExpiration(), + getExchange(), + getRoutingKey(), + transfer->getMessageId(), + transfer->getCorrelationId(), + transfer->getReplyTo(), + transfer->getContentType(), + transfer->getContentEncoding(), + transfer->getUserId(), + transfer->getAppId(), + transfer->getTransactionId(), + transfer->getSecurityToken(), + transfer->getApplicationHeaders(), + framing::Content(REFERENCE, refname), + transfer->getMandatory())); + ReferencePtr newRef(new Reference(refname)); + Reference::AppendPtr newAppend(new MessageAppendBody(channel.getVersion(), refname, content)); + newRef->append(newAppend); + MessageMessage newMsg(const_cast<ConnectionToken*>(getPublisher()), 0, newTransfer, newRef); + newMsg.transferMessage(channel, consumerTag, framesize); + return; + } + // Close any reference data + if (!body.isInline()){ + // Close + channel.send(new MessageCloseBody(channel.getVersion(), reference->getId())); + } +} + +void MessageMessage::deliver( + framing::ChannelAdapter& channel, + const std::string& consumerTag, + uint64_t /*deliveryTag*/, + uint32_t framesize) +{ + transferMessage(channel, consumerTag, framesize); +} + +void MessageMessage::sendGetOk( + const framing::MethodContext& context, + const std::string& destination, + uint32_t /*messageCount*/, + uint64_t /*deliveryTag*/, + uint32_t framesize) +{ + framing::ChannelAdapter* channel = context.channel; + transferMessage(*channel, destination, framesize); +} + +bool MessageMessage::isComplete() +{ + return true; +} + +uint64_t MessageMessage::contentSize() const +{ + if (transfer->getBody().isInline()) + return transfer->getBody().getValue().size(); + else + return reference->getSize(); +} + +qpid::framing::BasicHeaderProperties* MessageMessage::getHeaderProperties() +{ + return 0; // FIXME aconway 2007-02-05: +} + +const FieldTable& MessageMessage::getApplicationHeaders() +{ + return transfer->getApplicationHeaders(); +} +bool MessageMessage::isPersistent() +{ + return transfer->getDeliveryMode() == PERSISTENT; +} + +uint32_t MessageMessage::encodedSize() +{ + THROW_QPID_ERROR(INTERNAL_ERROR, "Unfinished"); + return 0; // FIXME aconway 2007-02-05: +} + +uint32_t MessageMessage::encodedHeaderSize() +{ + THROW_QPID_ERROR(INTERNAL_ERROR, "Unfinished"); + return 0; // FIXME aconway 2007-02-05: +} + +uint32_t MessageMessage::encodedContentSize() +{ + THROW_QPID_ERROR(INTERNAL_ERROR, "Unfinished"); + return 0; // FIXME aconway 2007-02-05: +} + +uint64_t MessageMessage::expectedContentSize() +{ + THROW_QPID_ERROR(INTERNAL_ERROR, "Unfinished"); + return 0; // FIXME aconway 2007-02-05: +} + + +}} // namespace qpid::broker diff --git a/cpp/lib/broker/BrokerMessageMessage.h b/cpp/lib/broker/BrokerMessageMessage.h new file mode 100644 index 0000000000..8a2ff3a063 --- /dev/null +++ b/cpp/lib/broker/BrokerMessageMessage.h @@ -0,0 +1,91 @@ +#ifndef _broker_BrokerMessageMessage_h +#define _broker_BrokerMessageMessage_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 "BrokerMessageBase.h" +#include "MessageTransferBody.h" +#include "amqp_types.h" + +#include <vector> + +namespace qpid { + +namespace framing { +class MessageTransferBody; +} + +namespace broker { +class ConnectionToken; +class Reference; + +class MessageMessage: public Message{ + public: + typedef boost::shared_ptr<MessageMessage> shared_ptr; + typedef boost::shared_ptr<framing::MessageTransferBody> TransferPtr; + typedef boost::shared_ptr<Reference> ReferencePtr; + + MessageMessage(ConnectionToken* publisher, framing::RequestId, TransferPtr transfer); + MessageMessage(ConnectionToken* publisher, framing::RequestId, TransferPtr transfer, ReferencePtr reference); + + // Default destructor okay + + framing::RequestId getRequestId() {return requestId; } + TransferPtr getTransfer() { return transfer; } + ReferencePtr getReference() { return reference; } + + void deliver(framing::ChannelAdapter& channel, + const std::string& consumerTag, + uint64_t deliveryTag, + uint32_t framesize); + + void sendGetOk(const framing::MethodContext& context, + const std::string& destination, + uint32_t messageCount, + uint64_t deliveryTag, + uint32_t framesize); + + bool isComplete(); + + uint64_t contentSize() const; + framing::BasicHeaderProperties* getHeaderProperties(); + const framing::FieldTable& getApplicationHeaders(); + bool isPersistent(); + + uint32_t encodedSize(); + uint32_t encodedHeaderSize(); + uint32_t encodedContentSize(); + uint64_t expectedContentSize(); + + private: + void transferMessage(framing::ChannelAdapter& channel, + const std::string& consumerTag, + uint32_t framesize); + + framing::RequestId requestId; + const TransferPtr transfer; + const ReferencePtr reference; +}; + +}} + + +#endif /*!_broker_BrokerMessage_h*/ diff --git a/cpp/lib/broker/BrokerQueue.cpp b/cpp/lib/broker/BrokerQueue.cpp new file mode 100644 index 0000000000..31309bd6c5 --- /dev/null +++ b/cpp/lib/broker/BrokerQueue.cpp @@ -0,0 +1,258 @@ +/* + * + * 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/format.hpp> + +#include <BrokerQueue.h> +#include <MessageStore.h> +#include <sys/Monitor.h> +#include <sys/Time.h> +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::framing; +using boost::format; + +Queue::Queue(const string& _name, uint32_t _autodelete, + MessageStore* const _store, + const ConnectionToken* const _owner) : + + name(_name), + autodelete(_autodelete), + store(_store), + owner(_owner), + queueing(false), + dispatching(false), + next(0), + lastUsed(0), + exclusive(0), + persistenceId(0) +{ + if(autodelete) lastUsed = now()/TIME_MSEC; +} + +Queue::~Queue(){ + for(Binding* b = bindings.front(); !bindings.empty(); b = bindings.front()){ + b->cancel(); + bindings.pop(); + } +} + +void Queue::bound(Binding* b){ + bindings.push(b); +} + +void Queue::deliver(Message::shared_ptr& msg){ + enqueue(0, msg, 0); + process(msg); +} + +void Queue::recover(Message::shared_ptr& msg){ + push(msg); + if (store && msg->expectedContentSize() != msg->encodedContentSize()) { + //content has not been loaded, need to ensure that lazy loading mode is set: + //TODO: find a nicer way to do this + msg->releaseContent(store); + } +} + +void Queue::process(Message::shared_ptr& msg){ + Mutex::ScopedLock locker(lock); + if(queueing || !dispatch(msg)){ + push(msg); + } +} + +bool Queue::dispatch(Message::shared_ptr& msg){ + if(consumers.empty()){ + return false; + }else if(exclusive){ + if(!exclusive->deliver(msg)){ + std::cout << "WARNING: Dropping undeliverable message from queue with exclusive consumer." << std::endl; + } + return true; + }else{ + //deliver to next consumer + next = next % consumers.size(); + Consumer* c = consumers[next]; + int start = next; + while(c){ + next++; + if(c->deliver(msg)) return true; + + next = next % consumers.size(); + c = next == start ? 0 : consumers[next]; + } + return false; + } +} + +bool Queue::startDispatching(){ + Mutex::ScopedLock locker(lock); + if(queueing && !dispatching){ + dispatching = true; + return true; + }else{ + return false; + } +} + +void Queue::dispatch(){ + bool proceed = startDispatching(); + while(proceed){ + Mutex::ScopedLock locker(lock); + if(!messages.empty() && dispatch(messages.front())){ + pop(); + }else{ + dispatching = false; + proceed = false; + queueing = !messages.empty(); + } + } +} + +void Queue::consume(Consumer* c, bool requestExclusive){ + Mutex::ScopedLock locker(lock); + if(exclusive) + throw ChannelException( + 403, format("Queue '%s' has an exclusive consumer." + " No more consumers allowed.") % getName()); + if(requestExclusive) { + if(!consumers.empty()) + throw ChannelException( + 403, format("Queue '%s' already has conumers." + "Exclusive access denied.") %getName()); + exclusive = c; + } + if(autodelete && consumers.empty()) lastUsed = 0; + consumers.push_back(c); +} + +void Queue::cancel(Consumer* c){ + Mutex::ScopedLock locker(lock); + Consumers::iterator i = std::find(consumers.begin(), consumers.end(), c); + if (i != consumers.end()) + consumers.erase(i); + if(autodelete && consumers.empty()) lastUsed = now()*TIME_MSEC; + if(exclusive == c) exclusive = 0; +} + +Message::shared_ptr Queue::dequeue(){ + Mutex::ScopedLock locker(lock); + Message::shared_ptr msg; + if(!messages.empty()){ + msg = messages.front(); + pop(); + } + return msg; +} + +uint32_t Queue::purge(){ + Mutex::ScopedLock locker(lock); + int count = messages.size(); + while(!messages.empty()) pop(); + return count; +} + +void Queue::pop(){ + if (policy.get()) policy->dequeued(messages.front()->contentSize()); + messages.pop(); +} + +void Queue::push(Message::shared_ptr& msg){ + queueing = true; + messages.push(msg); + if (policy.get()) { + policy->enqueued(msg->contentSize()); + if (policy->limitExceeded()) { + msg->releaseContent(store); + } + } +} + +uint32_t Queue::getMessageCount() const{ + Mutex::ScopedLock locker(lock); + return messages.size(); +} + +uint32_t Queue::getConsumerCount() const{ + Mutex::ScopedLock locker(lock); + return consumers.size(); +} + +bool Queue::canAutoDelete() const{ + Mutex::ScopedLock locker(lock); + return lastUsed && (now()*TIME_MSEC - lastUsed > autodelete); +} + +void Queue::enqueue(TransactionContext* ctxt, Message::shared_ptr& msg, const string * const xid) +{ + if (msg->isPersistent() && store) { + store->enqueue(ctxt, msg.get(), *this, xid); + } +} + +void Queue::dequeue(TransactionContext* ctxt, Message::shared_ptr& msg, const string * const xid) +{ + if (msg->isPersistent() && store) { + store->dequeue(ctxt, msg.get(), *this, xid); + } +} + +namespace +{ + const std::string qpidMaxSize("qpid.max_size"); + const std::string qpidMaxCount("qpid.max_count"); +} + +void Queue::create(const FieldTable& settings) +{ + if (store) { + store->create(*this, settings); + } + configure(settings); +} + +void Queue::configure(const FieldTable& settings) +{ + QueuePolicy* _policy = new QueuePolicy(settings); + if (_policy->getMaxCount() || _policy->getMaxSize()) { + setPolicy(std::auto_ptr<QueuePolicy>(_policy)); + } +} + +void Queue::destroy() +{ + if (store) { + store->destroy(*this); + } +} + +void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) +{ + policy = _policy; +} + +const QueuePolicy* const Queue::getPolicy() +{ + return policy.get(); +} diff --git a/cpp/lib/broker/BrokerQueue.h b/cpp/lib/broker/BrokerQueue.h new file mode 100644 index 0000000000..12f5815027 --- /dev/null +++ b/cpp/lib/broker/BrokerQueue.h @@ -0,0 +1,151 @@ +#ifndef _broker_BrokerQueue_h +#define _broker_BrokerQueue_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 <vector> +#include <memory> +#include <queue> +#include <boost/shared_ptr.hpp> +#include <amqp_types.h> +#include <Binding.h> +#include <ConnectionToken.h> +#include <Consumer.h> +#include <BrokerMessage.h> +#include <FieldTable.h> +#include <sys/Monitor.h> +#include <QueuePolicy.h> + +// TODO aconway 2007-02-06: Use auto_ptr and boost::ptr_vector to +// enforce ownership of Consumers. + +namespace qpid { + namespace broker { + class MessageStore; + + /** + * Thrown when exclusive access would be violated. + */ + using std::string; + + /** + * The brokers representation of an amqp queue. Messages are + * delivered to a queue from where they can be dispatched to + * registered consumers or be stored until dequeued or until one + * or more consumers registers. + */ + class Queue{ + typedef std::vector<Consumer*> Consumers; + typedef std::queue<Binding*> Bindings; + typedef std::queue<Message::shared_ptr> Messages; + + const string name; + const uint32_t autodelete; + MessageStore* const store; + const ConnectionToken* const owner; + Consumers consumers; + Bindings bindings; + Messages messages; + bool queueing; + bool dispatching; + int next; + mutable qpid::sys::Mutex lock; + int64_t lastUsed; + Consumer* exclusive; + mutable uint64_t persistenceId; + std::auto_ptr<QueuePolicy> policy; + + void pop(); + void push(Message::shared_ptr& msg); + bool startDispatching(); + bool dispatch(Message::shared_ptr& msg); + void setPolicy(std::auto_ptr<QueuePolicy> policy); + + public: + + typedef boost::shared_ptr<Queue> shared_ptr; + + typedef std::vector<shared_ptr> vector; + + Queue(const string& name, uint32_t autodelete = 0, + MessageStore* const store = 0, + const ConnectionToken* const owner = 0); + ~Queue(); + + void create(const qpid::framing::FieldTable& settings); + void configure(const qpid::framing::FieldTable& settings); + void destroy(); + /** + * Informs the queue of a binding that should be cancelled on + * destruction of the queue. + */ + void bound(Binding* b); + /** + * Delivers a message to the queue. Will record it as + * enqueued if persistent then process it. + */ + void deliver(Message::shared_ptr& msg); + /** + * Dispatches the messages immediately to a consumer if + * one is available or stores it for later if not. + */ + void process(Message::shared_ptr& msg); + /** + * Used during recovery to add stored messages back to the queue + */ + void recover(Message::shared_ptr& msg); + /** + * Dispatch any queued messages providing there are + * consumers for them. Only one thread can be dispatching + * at any time, but this method (rather than the caller) + * is responsible for ensuring that. + */ + void dispatch(); + void consume(Consumer* c, bool exclusive = false); + void cancel(Consumer* c); + uint32_t purge(); + uint32_t getMessageCount() const; + uint32_t getConsumerCount() const; + inline const string& getName() const { return name; } + inline const bool isExclusiveOwner(const ConnectionToken* const o) const { return o == owner; } + inline bool hasExclusiveConsumer() const { return exclusive; } + inline uint64_t getPersistenceId() const { return persistenceId; } + inline void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; } + + bool canAutoDelete() const; + + void enqueue(TransactionContext* ctxt, Message::shared_ptr& msg, const string * const xid); + /** + * dequeue from store (only done once messages is acknowledged) + */ + void dequeue(TransactionContext* ctxt, Message::shared_ptr& msg, const string * const xid); + /** + * dequeues from memory only + */ + Message::shared_ptr dequeue(); + + const QueuePolicy* const getPolicy(); + }; + } +} + + +#endif /*!_broker_BrokerQueue_h*/ diff --git a/cpp/lib/broker/BrokerSingleton.cpp b/cpp/lib/broker/BrokerSingleton.cpp new file mode 100644 index 0000000000..4571764850 --- /dev/null +++ b/cpp/lib/broker/BrokerSingleton.cpp @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "BrokerSingleton.h" + +namespace qpid { +namespace broker { + +BrokerSingleton::BrokerSingleton() { + if (broker.get() == 0) + broker = Broker::create(); + Broker::shared_ptr::operator=(broker); +} + +BrokerSingleton::~BrokerSingleton() { + broker->shutdown(); +} + +Broker::shared_ptr BrokerSingleton::broker; + +}} // namespace qpid::broker diff --git a/cpp/lib/broker/BrokerSingleton.h b/cpp/lib/broker/BrokerSingleton.h new file mode 100644 index 0000000000..139e02a5fd --- /dev/null +++ b/cpp/lib/broker/BrokerSingleton.h @@ -0,0 +1,52 @@ +#ifndef _broker_BrokerSingleton_h +#define _broker_BrokerSingleton_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Broker.h" + +namespace qpid { +namespace broker { + +/** + * BrokerSingleton is a smart pointer to a process-wide singleton broker + * started on an os-chosen port. The broker starts the first time + * an instance of BrokerSingleton is created and runs untill the process exits. + * + * Useful for unit tests that want to share a broker between multiple + * tests to reduce overhead of starting/stopping a broker for every test. + * + * Tests that need a new broker can call Broker::create directly. + * + * THREAD UNSAFE. + */ +class BrokerSingleton : public Broker::shared_ptr +{ + public: + BrokerSingleton(); + ~BrokerSingleton(); + private: + static Broker::shared_ptr broker; +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_BrokerSingleton_h*/ diff --git a/cpp/lib/broker/CompletionHandler.h b/cpp/lib/broker/CompletionHandler.h new file mode 100644 index 0000000000..9d51656282 --- /dev/null +++ b/cpp/lib/broker/CompletionHandler.h @@ -0,0 +1,39 @@ +#ifndef _broker_CompletionHandler_h +#define _broker_CompletionHandler_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +namespace qpid { +namespace broker { + +/** + * Callback interface to handle completion of a message. + */ +class CompletionHandler +{ + public: + virtual ~CompletionHandler(){} + virtual void complete(Message::shared_ptr) = 0; +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_CompletionHandler_h*/ diff --git a/cpp/lib/broker/Configuration.cpp b/cpp/lib/broker/Configuration.cpp new file mode 100644 index 0000000000..e83c359f2d --- /dev/null +++ b/cpp/lib/broker/Configuration.cpp @@ -0,0 +1,252 @@ +/* + * + * 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 <Configuration.h> +#include <string.h> +#include <config.h> + +using namespace qpid::broker; +using namespace std; + +Configuration::Configuration() : + daemon('d', "daemon", "Run as system daemon, detached from terminal.", false), + trace('t', "trace", "Print incoming & outgoing frames to the console", false), + port('p', "port", "Set the port to listen on (default=5672)", 5672), + workerThreads("worker-threads", "Set the number of worker threads to use (default=5).", 5), + maxConnections("max-connections", "Set the maximum number of connections the broker can accept (default=500).", 500), + connectionBacklog("connection-backlog", "Set the connection backlog for the servers socket (default=10)", 10), + store('s', "store", "Set the message store module to use (default='' which implies no store)", ""), + stagingThreshold("staging-threshold", "Set the message size threshold above which messages will be written to disk as they arrive (default=5,000,000)", 5000000), + help("help", "Print usage information", false), + version("version", "Print version information", false) +{ + options.push_back(&daemon); + options.push_back(&trace); + options.push_back(&port); + options.push_back(&workerThreads); + options.push_back(&maxConnections); + options.push_back(&connectionBacklog); + options.push_back(&store); + options.push_back(&stagingThreshold); + options.push_back(&help); + options.push_back(&version); +} + +Configuration::~Configuration(){} + +void Configuration::parse(char const *progName, int argc, char** argv){ + programName = progName; + int position = 1; + while(position < argc){ + bool matched(false); + for(op_iterator i = options.begin(); i < options.end() && !matched; i++){ + matched = (*i)->parse(position, argv, argc); + } + if(!matched) { + throw BadOptionException( + std::string("Unrecognised option: ")+argv[position]); + } + } +} + +void Configuration::usage(){ + std::cout << "Usage: " << programName << " [OPTION]..." << std::endl + << "Start the Qpid AMQP broker daemon." << std::endl << std::endl + << "Options:" << std::endl; + for(op_iterator i = options.begin(); i < options.end(); i++){ + (*i)->print(std::cout); + } + + std::cout << std::endl << "Report bugs to <" << PACKAGE_BUGREPORT << ">." + << std::endl; +} + +bool Configuration::isHelp() const { + return help.getValue(); +} + +bool Configuration::isVersion() const { + return version.getValue(); +} + +bool Configuration::isDaemon() const { + return daemon.getValue(); +} + +bool Configuration::isTrace() const { + return trace.getValue(); +} + +int Configuration::getPort() const { + return port.getValue(); +} + +int Configuration::getWorkerThreads() const { + return workerThreads.getValue(); +} + +int Configuration::getMaxConnections() const { + return maxConnections.getValue(); +} + +int Configuration::getConnectionBacklog() const { + return connectionBacklog.getValue(); +} + +const std::string& Configuration::getStore() const { + return store.getValue(); +} + +long Configuration::getStagingThreshold() const { + return stagingThreshold.getValue(); +} + + +Configuration::Option::Option(const char _flag, const string& _name, const string& _desc) : + flag(string("-") + _flag), name("--" +_name), desc(_desc) {} + +Configuration::Option::Option(const string& _name, const string& _desc) : + flag(""), name("--" + _name), desc(_desc) {} + +Configuration::Option::~Option(){} + +bool Configuration::Option::match(const string& arg){ + return flag == arg || name == arg; +} + +bool Configuration::Option::parse(int& i, char** argv, int argc){ + const string arg(argv[i]); + if(match(arg)){ + if(needsValue()){ + if(++i < argc) setValue(argv[i]); + else throw ParseException("Argument " + arg + " requires a value!"); + }else{ + setValue(""); + } + i++; + return true; + }else{ + return false; + } +} + +void Configuration::Option::print(ostream& out) const { + out << " "; + if(flag.length() > 0){ + out << flag << ", "; + } else { + out << " "; + } + out << name; + if(needsValue()) out << " <value>"; + out << std::endl; + out << " " << desc << std::endl; +} + + +// String Option: + +Configuration::StringOption::StringOption(const char _flag, const string& _name, const string& _desc, const string _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::StringOption::StringOption(const string& _name, const string& _desc, const string _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::StringOption::~StringOption(){} + +const string& Configuration::StringOption::getValue() const { + return value; +} + +bool Configuration::StringOption::needsValue() const { + return true; +} + +void Configuration::StringOption::setValue(const std::string& _value){ + value = _value; +} + +// Int Option: + +Configuration::IntOption::IntOption(const char _flag, const string& _name, const string& _desc, const int _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::IntOption::IntOption(const string& _name, const string& _desc, const int _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::IntOption::~IntOption(){} + +int Configuration::IntOption::getValue() const { + return value; +} + +bool Configuration::IntOption::needsValue() const { + return true; +} + +void Configuration::IntOption::setValue(const std::string& _value){ + value = atoi(_value.c_str()); +} + +// Long Option: + +Configuration::LongOption::LongOption(const char _flag, const string& _name, const string& _desc, const long _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::LongOption::LongOption(const string& _name, const string& _desc, const long _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::LongOption::~LongOption(){} + +long Configuration::LongOption::getValue() const { + return value; +} + +bool Configuration::LongOption::needsValue() const { + return true; +} + +void Configuration::LongOption::setValue(const std::string& _value){ + value = atol(_value.c_str()); +} + +// Bool Option: + +Configuration::BoolOption::BoolOption(const char _flag, const string& _name, const string& _desc, const bool _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::BoolOption::BoolOption(const string& _name, const string& _desc, const bool _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::BoolOption::~BoolOption(){} + +bool Configuration::BoolOption::getValue() const { + return value; +} + +bool Configuration::BoolOption::needsValue() const { + return false; +} + +void Configuration::BoolOption::setValue(const std::string& /*not required*/){ + //BoolOptions have no value. The fact that the option is specified + //implies the value is true. + value = true; +} diff --git a/cpp/lib/broker/Configuration.h b/cpp/lib/broker/Configuration.h new file mode 100644 index 0000000000..27c743c8f0 --- /dev/null +++ b/cpp/lib/broker/Configuration.h @@ -0,0 +1,171 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Configuration_ +#define _Configuration_ + +#include <cstdlib> +#include <iostream> +#include <vector> +#include <Exception.h> + +namespace qpid { +namespace broker { +class Configuration{ + + class Option { + const std::string flag; + const std::string name; + const std::string desc; + + bool match(const std::string& arg); + + protected: + virtual bool needsValue() const = 0; + virtual void setValue(const std::string& value) = 0; + + public: + Option(const char flag, const std::string& name, const std::string& desc); + Option(const std::string& name, const std::string& desc); + virtual ~Option(); + + bool parse(int& i, char** argv, int argc); + void print(std::ostream& out) const; + }; + + class IntOption : public Option{ + const int defaultValue; + int value; + public: + IntOption(char flag, const std::string& name, const std::string& desc, const int value = 0); + IntOption(const std::string& name, const std::string& desc, const int value = 0); + virtual ~IntOption(); + + int getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + virtual void setValue(int _value) { value = _value; } + }; + + class LongOption : public Option{ + const long defaultValue; + int value; + public: + LongOption(char flag, const std::string& name, const std::string& desc, const long value = 0); + LongOption(const std::string& name, const std::string& desc, const long value = 0); + virtual ~LongOption(); + + long getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + virtual void setValue(int _value) { value = _value; } + }; + + class StringOption : public Option{ + const std::string defaultValue; + std::string value; + public: + StringOption(char flag, const std::string& name, const std::string& desc, const std::string value = ""); + StringOption(const std::string& name, const std::string& desc, const std::string value = ""); + virtual ~StringOption(); + + const std::string& getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + }; + + class BoolOption : public Option{ + const bool defaultValue; + bool value; + public: + BoolOption(char flag, const std::string& name, const std::string& desc, const bool value = 0); + BoolOption(const std::string& name, const std::string& desc, const bool value = 0); + virtual ~BoolOption(); + + bool getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + virtual void setValue(bool _value) { value = _value; } + }; + + BoolOption daemon; + BoolOption trace; + IntOption port; + IntOption workerThreads; + IntOption maxConnections; + IntOption connectionBacklog; + StringOption store; + LongOption stagingThreshold; + BoolOption help; + BoolOption version; + char const *programName; + + typedef std::vector<Option*>::iterator op_iterator; + std::vector<Option*> options; + + public: + + struct BadOptionException : public Exception { + template<class T> + BadOptionException(const T& msg) : Exception(msg) {} + }; + + + class ParseException : public Exception { + public: + template <class T> + ParseException(const T& msg) : Exception(msg) {} + }; + + + Configuration(); + ~Configuration(); + + void parse(char const*, int argc, char** argv); + + bool isHelp() const; + bool isVersion() const; + bool isDaemon() const; + bool isTrace() const; + int getPort() const; + int getWorkerThreads() const; + int getMaxConnections() const; + int getConnectionBacklog() const; + const std::string& getStore() const; + long getStagingThreshold() const; + + void setHelp(bool b) { help.setValue(b); } + void setVersion(bool b) { version.setValue(b); } + void setDaemon(bool b) { daemon.setValue(b); } + void setTrace(bool b) { trace.setValue(b); } + void setPort(int i) { port.setValue(i); } + void setWorkerThreads(int i) { workerThreads.setValue(i); } + void setMaxConnections(int i) { maxConnections.setValue(i); } + void setConnectionBacklog(int i) { connectionBacklog.setValue(i); } + void setStore(const std::string& s) { store.setValue(s); } + void setStagingThreshold(long l) { stagingThreshold.setValue(l); } + + void usage(); +}; +} +} + + +#endif diff --git a/cpp/lib/broker/Connection.cpp b/cpp/lib/broker/Connection.cpp new file mode 100644 index 0000000000..ae0114cba9 --- /dev/null +++ b/cpp/lib/broker/Connection.cpp @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <assert.h> + +#include "Connection.h" +#include "BrokerChannel.h" +#include "AMQP_ClientProxy.h" +#include "BrokerAdapter.h" + +using namespace boost; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace qpid { +namespace broker { + +Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_) : + broker(broker_), + out(out_), + framemax(65536), + heartbeat(0), + client(0), + timeout(broker.getTimeout()), + stagingThreshold(broker.getStagingThreshold()) +{} + + +Queue::shared_ptr Connection::getQueue(const string& name, uint16_t channel){ + Queue::shared_ptr queue; + if (name.empty()) { + queue = getChannel(channel).getDefaultQueue(); + if (!queue) throw ConnectionException( 530, "Queue must be specified or previously declared" ); + } else { + queue = broker.getQueues().find(name); + if (queue == 0) { + throw ChannelException( 404, "Queue not found: " + name); + } + } + return queue; +} + + +Exchange::shared_ptr Connection::findExchange(const string& name){ + return broker.getExchanges().get(name); +} + + +void Connection::received(framing::AMQFrame* frame){ + getChannel(frame->getChannel()).handleBody(frame->getBody()); +} + +void Connection::close( + ReplyCode code, const string& text, ClassId classId, MethodId methodId) +{ + client->close(code, text, classId, methodId); + getOutput().close(); +} + +void Connection::initiated(framing::ProtocolInitiation* header) { + version = ProtocolVersion(header->getMajor(), header->getMinor()); + FieldTable properties; + string mechanisms("PLAIN"); + string locales("en_US"); + getChannel(0).init(0, *out, getVersion()); + client = &getChannel(0).getAdatper().getProxy().getConnection(); + client->start( + header->getMajor(), header->getMinor(), + properties, mechanisms, locales); +} + +void Connection::idleOut(){} + +void Connection::idleIn(){} + +void Connection::closed(){ + try { + while (!exclusiveQueues.empty()) { + broker.getQueues().destroy(exclusiveQueues.front()->getName()); + exclusiveQueues.erase(exclusiveQueues.begin()); + } + } catch(std::exception& e) { + std::cout << "Caught unhandled exception while closing session: " << + e.what() << std::endl; + assert(0); + } +} + +void Connection::closeChannel(uint16_t id) { + ChannelMap::iterator i = channels.find(id); + if (i != channels.end()) + i->close(); +} + + +Channel& Connection::getChannel(ChannelId id) { + ChannelMap::iterator i = channels.find(id); + if (i == channels.end()) { + i = channels.insert( + id, new Channel( + *this, id, framemax, broker.getQueues().getStore(), + broker.getStagingThreshold())).first; + } + return *i; +} + + +}} + diff --git a/cpp/lib/broker/Connection.h b/cpp/lib/broker/Connection.h new file mode 100644 index 0000000000..1314ccbd97 --- /dev/null +++ b/cpp/lib/broker/Connection.h @@ -0,0 +1,108 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Connection_ +#define _Connection_ + +#include <sstream> +#include <vector> + +#include <boost/ptr_container/ptr_map.hpp> + +#include <AMQFrame.h> +#include <AMQP_ServerOperations.h> +#include <AMQP_ClientProxy.h> +#include <sys/ConnectionOutputHandler.h> +#include <sys/ConnectionInputHandler.h> +#include <sys/TimeoutHandler.h> +#include "framing/ProtocolVersion.h" +#include "Broker.h" +#include "Exception.h" +#include "BrokerChannel.h" + +namespace qpid { +namespace broker { + +class Channel; + +class Connection : public sys::ConnectionInputHandler, + public ConnectionToken +{ + public: + Connection(sys::ConnectionOutputHandler* out, Broker& broker); + + /** Get a channel. Create if it does not already exist */ + Channel& getChannel(framing::ChannelId channel); + + /** Close a channel */ + void closeChannel(framing::ChannelId channel); + + /** Close the connection */ + void close(framing::ReplyCode code, const string& text, framing::ClassId classId, framing::MethodId methodId); + + sys::ConnectionOutputHandler& getOutput() const { return *out; } + framing::ProtocolVersion getVersion() const { return version; } + + uint32_t getFrameMax() const { return framemax; } + uint16_t getHeartbeat() const { return heartbeat; } + uint32_t getTimeout() const { return timeout; } + uint64_t getStagingThreshold() const { return stagingThreshold; } + + void setFrameMax(uint32_t fm) { framemax = fm; } + void setHeartbeat(uint16_t hb) { heartbeat = hb; } + + /** + * Get named queue, never returns 0. + * @return: named queue or default queue for channel if name="" + * @exception: ChannelException if no queue of that name is found. + * @exception: ConnectionException if name="" and channel has no default. + */ + Queue::shared_ptr getQueue(const string& name, uint16_t channel); + + Broker& broker; + std::vector<Queue::shared_ptr> exclusiveQueues; + + // ConnectionInputHandler methods + void received(framing::AMQFrame* frame); + void initiated(framing::ProtocolInitiation* header); + void idleOut(); + void idleIn(); + void closed(); + + private: + typedef boost::ptr_map<framing::ChannelId, Channel> ChannelMap; + + typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; + Exchange::shared_ptr findExchange(const string& name); + + framing::ProtocolVersion version; + ChannelMap channels; + sys::ConnectionOutputHandler* out; + uint32_t framemax; + uint16_t heartbeat; + framing::AMQP_ClientProxy::Connection* client; + const uint32_t timeout; //timeout for auto-deleted queues (in ms) + const uint64_t stagingThreshold; + +}; + +}} + +#endif diff --git a/cpp/lib/broker/ConnectionFactory.cpp b/cpp/lib/broker/ConnectionFactory.cpp new file mode 100644 index 0000000000..20485dd0e1 --- /dev/null +++ b/cpp/lib/broker/ConnectionFactory.cpp @@ -0,0 +1,43 @@ +/* + * + * 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 <ConnectionFactory.h> +#include <Connection.h> + +namespace qpid { +namespace broker { + + +ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) +{} + + +ConnectionFactory::~ConnectionFactory() +{ + broker.getCleaner().stop(); +} + +qpid::sys::ConnectionInputHandler* +ConnectionFactory::create(qpid::sys::ConnectionOutputHandler* out) +{ + return new Connection(out, broker); +} + +}} // namespace qpid::broker diff --git a/cpp/lib/broker/ConnectionFactory.h b/cpp/lib/broker/ConnectionFactory.h new file mode 100644 index 0000000000..9147384b2a --- /dev/null +++ b/cpp/lib/broker/ConnectionFactory.h @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionFactory_ +#define _ConnectionFactory_ + +#include "ConnectionInputHandlerFactory.h" + +namespace qpid { +namespace broker { +class Broker; + +class ConnectionFactory : public qpid::sys::ConnectionInputHandlerFactory +{ + public: + ConnectionFactory(Broker& b); + + virtual qpid::sys::ConnectionInputHandler* create( + qpid::sys::ConnectionOutputHandler* ctxt); + + virtual ~ConnectionFactory(); + + private: + Broker& broker; +}; + +}} + + +#endif diff --git a/cpp/lib/broker/ConnectionToken.h b/cpp/lib/broker/ConnectionToken.h new file mode 100644 index 0000000000..7e7f813d0e --- /dev/null +++ b/cpp/lib/broker/ConnectionToken.h @@ -0,0 +1,38 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionToken_ +#define _ConnectionToken_ + +namespace qpid { + namespace broker { + /** + * An empty interface allowing opaque implementations of some + * form of token to identify a connection. + */ + class ConnectionToken{ + public: + virtual ~ConnectionToken(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/Consumer.h b/cpp/lib/broker/Consumer.h new file mode 100644 index 0000000000..26deef4a26 --- /dev/null +++ b/cpp/lib/broker/Consumer.h @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Consumer_ +#define _Consumer_ + +#include <BrokerMessage.h> + +namespace qpid { + namespace broker { + class Consumer{ + public: + virtual bool deliver(Message::shared_ptr& msg) = 0; + virtual ~Consumer(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/Content.h b/cpp/lib/broker/Content.h new file mode 100644 index 0000000000..b65a454778 --- /dev/null +++ b/cpp/lib/broker/Content.h @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Content_ +#define _Content_ + +#include <boost/function.hpp> + +#include <AMQContentBody.h> +#include <Buffer.h> +#include <OutputHandler.h> + +namespace qpid { + +namespace framing { +class ChannelAdapter; +} + +namespace broker { +class Content{ + public: + typedef std::string DataBlock; + typedef boost::function1<void, const DataBlock&> SendFn; + + virtual ~Content(){} + + /** Add a block of data to the content */ + virtual void add(framing::AMQContentBody::shared_ptr data) = 0; + + /** Total size of content in bytes */ + virtual uint32_t size() = 0; + + /** + * Iterate over the content calling SendFn for each block. + * Subdivide blocks if necessary to ensure each block is + * <= framesize bytes long. + */ + virtual void send(framing::ChannelAdapter& channel, uint32_t framesize) = 0; + + //FIXME aconway 2007-02-07: This is inconsistently implemented + //find out what is needed. + virtual void encode(qpid::framing::Buffer& buffer) = 0; +}; +}} + + +#endif diff --git a/cpp/lib/broker/DeletingTxOp.cpp b/cpp/lib/broker/DeletingTxOp.cpp new file mode 100644 index 0000000000..25fe9c98db --- /dev/null +++ b/cpp/lib/broker/DeletingTxOp.cpp @@ -0,0 +1,45 @@ +/* + * + * 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 <DeletingTxOp.h> + +using namespace qpid::broker; + +DeletingTxOp::DeletingTxOp(TxOp* const _delegate) : delegate(_delegate){} + +bool DeletingTxOp::prepare(TransactionContext* ctxt) throw(){ + return delegate && delegate->prepare(ctxt); +} + +void DeletingTxOp::commit() throw(){ + if(delegate){ + delegate->commit(); + delete delegate; + delegate = 0; + } +} + +void DeletingTxOp::rollback() throw(){ + if(delegate){ + delegate->rollback(); + delete delegate; + delegate = 0; + } +} diff --git a/cpp/lib/broker/DeletingTxOp.h b/cpp/lib/broker/DeletingTxOp.h new file mode 100644 index 0000000000..3e026cd4ca --- /dev/null +++ b/cpp/lib/broker/DeletingTxOp.h @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DeletingTxOp_ +#define _DeletingTxOp_ + +#include <TxOp.h> + +namespace qpid { + namespace broker { + /** + * TxOp wrapper that will delegate calls & delete the object + * to which it delegates after completion of the transaction. + */ + class DeletingTxOp : public virtual TxOp{ + TxOp* delegate; + public: + DeletingTxOp(TxOp* const delegate); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~DeletingTxOp(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/Deliverable.h b/cpp/lib/broker/Deliverable.h new file mode 100644 index 0000000000..e33443555d --- /dev/null +++ b/cpp/lib/broker/Deliverable.h @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Deliverable_ +#define _Deliverable_ + +#include <BrokerQueue.h> + +namespace qpid { + namespace broker { + class Deliverable{ + public: + virtual void deliverTo(Queue::shared_ptr& queue) = 0; + virtual ~Deliverable(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/DeliverableMessage.cpp b/cpp/lib/broker/DeliverableMessage.cpp new file mode 100644 index 0000000000..b9c89da690 --- /dev/null +++ b/cpp/lib/broker/DeliverableMessage.cpp @@ -0,0 +1,33 @@ +/* + * + * 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 <DeliverableMessage.h> + +using namespace qpid::broker; + +DeliverableMessage::DeliverableMessage(Message::shared_ptr& _msg) : msg(_msg) +{ +} + +void DeliverableMessage::deliverTo(Queue::shared_ptr& queue) +{ + queue->deliver(msg); +} + diff --git a/cpp/lib/broker/DeliverableMessage.h b/cpp/lib/broker/DeliverableMessage.h new file mode 100644 index 0000000000..962f0da640 --- /dev/null +++ b/cpp/lib/broker/DeliverableMessage.h @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DeliverableMessage_ +#define _DeliverableMessage_ + +#include <Deliverable.h> +#include <BrokerMessage.h> +#include <BrokerQueue.h> + +namespace qpid { + namespace broker { + class DeliverableMessage : public Deliverable{ + Message::shared_ptr msg; + public: + DeliverableMessage(Message::shared_ptr& msg); + virtual void deliverTo(Queue::shared_ptr& queue); + virtual ~DeliverableMessage(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/DeliveryRecord.cpp b/cpp/lib/broker/DeliveryRecord.cpp new file mode 100644 index 0000000000..0d2e5325c5 --- /dev/null +++ b/cpp/lib/broker/DeliveryRecord.cpp @@ -0,0 +1,91 @@ +/* + * + * 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 <DeliveryRecord.h> +#include <BrokerChannel.h> + +using namespace qpid::broker; +using std::string; + +DeliveryRecord::DeliveryRecord(Message::shared_ptr _msg, + Queue::shared_ptr _queue, + const string _consumerTag, + const uint64_t _deliveryTag) : msg(_msg), + queue(_queue), + consumerTag(_consumerTag), + deliveryTag(_deliveryTag), + pull(false){} + +DeliveryRecord::DeliveryRecord(Message::shared_ptr _msg, + Queue::shared_ptr _queue, + const uint64_t _deliveryTag) : msg(_msg), + queue(_queue), + consumerTag(""), + deliveryTag(_deliveryTag), + pull(true){} + + +void DeliveryRecord::discard(TransactionContext* ctxt, const std::string* const xid) const{ + queue->dequeue(ctxt, msg, xid); +} + +void DeliveryRecord::discard() const{ + discard(0, 0); +} + +bool DeliveryRecord::matches(uint64_t tag) const{ + return deliveryTag == tag; +} + +bool DeliveryRecord::coveredBy(const AccumulatedAck* const range) const{ + return range->covers(deliveryTag); +} + +void DeliveryRecord::redeliver(Channel* const channel) const{ + if(pull){ + //if message was originally sent as response to get, we must requeue it + requeue(); + }else{ + channel->deliver(msg, consumerTag, deliveryTag); + } +} + +void DeliveryRecord::requeue() const{ + msg->redeliver(); + queue->process(msg); +} + +void DeliveryRecord::addTo(Prefetch* const prefetch) const{ + if(!pull){ + //ignore 'pulled' messages (i.e. those that were sent in + //response to get) when calculating prefetch + prefetch->size += msg->contentSize(); + prefetch->count++; + } +} + +void DeliveryRecord::subtractFrom(Prefetch* const prefetch) const{ + if(!pull){ + //ignore 'pulled' messages (i.e. those that were sent in + //response to get) when calculating prefetch + prefetch->size -= msg->contentSize(); + prefetch->count--; + } +} diff --git a/cpp/lib/broker/DeliveryRecord.h b/cpp/lib/broker/DeliveryRecord.h new file mode 100644 index 0000000000..bda2c2ec90 --- /dev/null +++ b/cpp/lib/broker/DeliveryRecord.h @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DeliveryRecord_ +#define _DeliveryRecord_ + +#include <algorithm> +#include <list> +#include <AccumulatedAck.h> +#include <BrokerMessage.h> +#include <Prefetch.h> +#include <BrokerQueue.h> + +namespace qpid { + namespace broker { + class Channel; + + /** + * Record of a delivery for which an ack is outstanding. + */ + class DeliveryRecord{ + mutable Message::shared_ptr msg; + mutable Queue::shared_ptr queue; + std::string consumerTag; + uint64_t deliveryTag; + bool pull; + + public: + DeliveryRecord(Message::shared_ptr msg, Queue::shared_ptr queue, const std::string consumerTag, const uint64_t deliveryTag); + DeliveryRecord(Message::shared_ptr msg, Queue::shared_ptr queue, const uint64_t deliveryTag); + + void discard() const; + void discard(TransactionContext* ctxt, const std::string* const xid) const; + bool matches(uint64_t tag) const; + bool coveredBy(const AccumulatedAck* const range) const; + void requeue() const; + void redeliver(Channel* const) const; + void addTo(Prefetch* const prefetch) const; + void subtractFrom(Prefetch* const prefetch) const; + }; + + typedef std::list<DeliveryRecord>::iterator ack_iterator; + } +} + + +#endif diff --git a/cpp/lib/broker/DirectExchange.cpp b/cpp/lib/broker/DirectExchange.cpp new file mode 100644 index 0000000000..c898ae8d7e --- /dev/null +++ b/cpp/lib/broker/DirectExchange.cpp @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <DirectExchange.h> +#include <ExchangeBinding.h> +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +DirectExchange::DirectExchange(const string& _name) : Exchange(_name) { + +} + +void DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args){ + Mutex::ScopedLock l(lock); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + std::vector<Queue::shared_ptr>::iterator i = find(queues.begin(), queues.end(), queue); + if(i == queues.end()){ + bindings[routingKey].push_back(queue); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); + } +} + +void DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ + Mutex::ScopedLock l(lock); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + + std::vector<Queue::shared_ptr>::iterator i = find(queues.begin(), queues.end(), queue); + if(i < queues.end()){ + queues.erase(i); + if(queues.empty()){ + bindings.erase(routingKey); + } + } +} + +void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ + Mutex::ScopedLock l(lock); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + int count(0); + for(std::vector<Queue::shared_ptr>::iterator i = queues.begin(); i != queues.end(); i++, count++){ + msg.deliverTo(*i); + } + if(!count){ + std::cout << "WARNING: DirectExchange " << getName() << " could not route message with key " << routingKey << std::endl; + } +} + +DirectExchange::~DirectExchange(){ + +} + + +const std::string DirectExchange::typeName("direct"); diff --git a/cpp/lib/broker/DirectExchange.h b/cpp/lib/broker/DirectExchange.h new file mode 100644 index 0000000000..a7ef5aca9e --- /dev/null +++ b/cpp/lib/broker/DirectExchange.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _DirectExchange_ +#define _DirectExchange_ + +#include <map> +#include <vector> +#include <BrokerExchange.h> +#include <FieldTable.h> +#include <BrokerMessage.h> +#include <sys/Monitor.h> +#include <BrokerQueue.h> + +namespace qpid { +namespace broker { + class DirectExchange : public virtual Exchange{ + std::map<string, std::vector<Queue::shared_ptr> > bindings; + qpid::sys::Mutex lock; + + public: + static const std::string typeName; + + DirectExchange(const std::string& name); + + virtual std::string getType(){ return typeName; } + + virtual void bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual ~DirectExchange(); + }; +} +} + + +#endif diff --git a/cpp/lib/broker/ExchangeBinding.cpp b/cpp/lib/broker/ExchangeBinding.cpp new file mode 100644 index 0000000000..bf2102414d --- /dev/null +++ b/cpp/lib/broker/ExchangeBinding.cpp @@ -0,0 +1,35 @@ +/* + * + * 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 <ExchangeBinding.h> +#include <BrokerExchange.h> + +using namespace qpid::broker; +using namespace qpid::framing; + +ExchangeBinding::ExchangeBinding(Exchange* _e, Queue::shared_ptr _q, const string& _key, const FieldTable* _args) : e(_e), q(_q), key(_key), args(_args){} + +void ExchangeBinding::cancel(){ + e->unbind(q, key, args); + delete this; +} + +ExchangeBinding::~ExchangeBinding(){ +} diff --git a/cpp/lib/broker/ExchangeBinding.h b/cpp/lib/broker/ExchangeBinding.h new file mode 100644 index 0000000000..2afaa89552 --- /dev/null +++ b/cpp/lib/broker/ExchangeBinding.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ExchangeBinding_ +#define _ExchangeBinding_ + +#include <Binding.h> +#include <FieldTable.h> +#include <BrokerQueue.h> + +namespace qpid { + namespace broker { + class Exchange; + class Queue; + + class ExchangeBinding : public virtual Binding{ + Exchange* e; + Queue::shared_ptr q; + const string key; + const qpid::framing::FieldTable* args; + public: + ExchangeBinding(Exchange* _e, Queue::shared_ptr _q, const string& _key, const qpid::framing::FieldTable* _args); + virtual void cancel(); + virtual ~ExchangeBinding(); + }; + } +} + + +#endif + diff --git a/cpp/lib/broker/ExchangeRegistry.cpp b/cpp/lib/broker/ExchangeRegistry.cpp new file mode 100644 index 0000000000..3e5ed89b54 --- /dev/null +++ b/cpp/lib/broker/ExchangeRegistry.cpp @@ -0,0 +1,76 @@ +/* + * + * 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 <ExchangeRegistry.h> +#include <DirectExchange.h> +#include <FanOutExchange.h> +#include <HeadersExchange.h> +#include <TopicExchange.h> + +using namespace qpid::broker; +using namespace qpid::sys; +using std::pair; + +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type) throw(UnknownExchangeTypeException){ + Mutex::ScopedLock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i == exchanges.end()) { + Exchange::shared_ptr exchange; + + if(type == TopicExchange::typeName){ + exchange = Exchange::shared_ptr(new TopicExchange(name)); + }else if(type == DirectExchange::typeName){ + exchange = Exchange::shared_ptr(new DirectExchange(name)); + }else if(type == FanOutExchange::typeName){ + exchange = Exchange::shared_ptr(new FanOutExchange(name)); + }else if (type == HeadersExchange::typeName) { + exchange = Exchange::shared_ptr(new HeadersExchange(name)); + }else{ + throw UnknownExchangeTypeException(); + } + exchanges[name] = exchange; + return std::pair<Exchange::shared_ptr, bool>(exchange, true); + } else { + return std::pair<Exchange::shared_ptr, bool>(i->second, false); + } +} + +void ExchangeRegistry::destroy(const string& name){ + Mutex::ScopedLock locker(lock); + exchanges.erase(name); +} + +Exchange::shared_ptr ExchangeRegistry::get(const string& name){ + Mutex::ScopedLock locker(lock); + Exchange::shared_ptr exchange =exchanges[name]; + if (!exchange) + throw ChannelException(404, "Exchange not found:" + name); + return exchange; +} + +namespace +{ +const std::string empty; +} + +Exchange::shared_ptr ExchangeRegistry::getDefault() +{ + return get(empty); +} diff --git a/cpp/lib/broker/ExchangeRegistry.h b/cpp/lib/broker/ExchangeRegistry.h new file mode 100644 index 0000000000..aeb32753df --- /dev/null +++ b/cpp/lib/broker/ExchangeRegistry.h @@ -0,0 +1,47 @@ +#ifndef _broker_ExchangeRegistry_h +#define _broker_ExchangeRegistry_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 <BrokerExchange.h> +#include <sys/Monitor.h> + +namespace qpid { +namespace broker { + struct UnknownExchangeTypeException{}; + + class ExchangeRegistry{ + typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap; + ExchangeMap exchanges; + qpid::sys::Mutex lock; + public: + std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type) throw(UnknownExchangeTypeException); + void destroy(const std::string& name); + Exchange::shared_ptr get(const std::string& name); + Exchange::shared_ptr getDefault(); + }; +} +} + + +#endif /*!_broker_ExchangeRegistry_h*/ diff --git a/cpp/lib/broker/FanOutExchange.cpp b/cpp/lib/broker/FanOutExchange.cpp new file mode 100644 index 0000000000..48afcc20d5 --- /dev/null +++ b/cpp/lib/broker/FanOutExchange.cpp @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <FanOutExchange.h> +#include <ExchangeBinding.h> +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +FanOutExchange::FanOutExchange(const std::string& _name) : Exchange(_name) {} + +void FanOutExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args){ + Mutex::ScopedLock locker(lock); + // Add if not already present. + Queue::vector::iterator i = std::find(bindings.begin(), bindings.end(), queue); + if (i == bindings.end()) { + bindings.push_back(queue); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); + } +} + +void FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* /*args*/){ + Mutex::ScopedLock locker(lock); + Queue::vector::iterator i = std::find(bindings.begin(), bindings.end(), queue); + if (i != bindings.end()) { + bindings.erase(i); + // TODO aconway 2006-09-14: What about the ExchangeBinding object? + // Don't we have to verify routingKey/args match? + } +} + +void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/){ + Mutex::ScopedLock locker(lock); + for(Queue::vector::iterator i = bindings.begin(); i != bindings.end(); ++i){ + msg.deliverTo(*i); + } +} + +FanOutExchange::~FanOutExchange() {} + +const std::string FanOutExchange::typeName("fanout"); diff --git a/cpp/lib/broker/FanOutExchange.h b/cpp/lib/broker/FanOutExchange.h new file mode 100644 index 0000000000..6dc70e69bb --- /dev/null +++ b/cpp/lib/broker/FanOutExchange.h @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _FanOutExchange_ +#define _FanOutExchange_ + +#include <map> +#include <vector> +#include <BrokerExchange.h> +#include <FieldTable.h> +#include <BrokerMessage.h> +#include <sys/Monitor.h> +#include <BrokerQueue.h> + +namespace qpid { +namespace broker { + +class FanOutExchange : public virtual Exchange { + std::vector<Queue::shared_ptr> bindings; + qpid::sys::Mutex lock; + + public: + static const std::string typeName; + + FanOutExchange(const std::string& name); + + virtual std::string getType(){ return typeName; } + + virtual void bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual ~FanOutExchange(); +}; + +} +} + + + +#endif diff --git a/cpp/lib/broker/HandlerImpl.h b/cpp/lib/broker/HandlerImpl.h new file mode 100644 index 0000000000..c55a36da45 --- /dev/null +++ b/cpp/lib/broker/HandlerImpl.h @@ -0,0 +1,71 @@ +#ifndef _broker_HandlerImpl_h +#define _broker_HandlerImpl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "BrokerChannel.h" +#include "AMQP_ClientProxy.h" + +namespace qpid { + +namespace framing { +class AMQP_ClientProxy; +} + +namespace broker { + +class Broker; +class Channel; +class Connection; + +/** + * A collection of references to the core objects required by an adapter, + * and a client proxy. + */ +struct CoreRefs +{ + CoreRefs(Channel& ch, Connection& c, Broker& b) + : channel(ch), connection(c), broker(b), proxy(ch) {} + + Channel& channel; + Connection& connection; + Broker& broker; + framing::AMQP_ClientProxy proxy; +}; + + +/** + * Base template for protocol handler implementations. + * Provides the core references and appropriate AMQP class proxy. + */ +template <class ProxyType> +struct HandlerImpl : public CoreRefs { + typedef HandlerImpl<ProxyType> HandlerImplType; + HandlerImpl(CoreRefs& parent) + : CoreRefs(parent), client(ProxyType::get(proxy)) {} + ProxyType client; +}; + + + +}} // namespace qpid::broker + + + +#endif /*!_broker_HandlerImpl_h*/ diff --git a/cpp/lib/broker/HeadersExchange.cpp b/cpp/lib/broker/HeadersExchange.cpp new file mode 100644 index 0000000000..acd344725a --- /dev/null +++ b/cpp/lib/broker/HeadersExchange.cpp @@ -0,0 +1,121 @@ +/* + * + * 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 <HeadersExchange.h> +#include <ExchangeBinding.h> +#include <Value.h> +#include <QpidError.h> +#include <algorithm> + + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +// TODO aconway 2006-09-20: More efficient matching algorithm. +// The current search algorithm really sucks. +// Fieldtables are heavy, maybe use shared_ptr to do handle-body. + +using namespace qpid::broker; + +namespace { + const std::string all("all"); + const std::string any("any"); + const std::string x_match("x-match"); +} + +HeadersExchange::HeadersExchange(const string& _name) : Exchange(_name) { } + +void HeadersExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args){ + Mutex::ScopedLock locker(lock); + std::string what = args->getString("x-match"); + if (what != all && what != any) { + THROW_QPID_ERROR(PROTOCOL_ERROR, "Invalid x-match value binding to headers exchange."); + } + bindings.push_back(Binding(*args, queue)); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); +} + +void HeadersExchange::unbind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* args){ + Mutex::ScopedLock locker(lock); + Bindings::iterator i = + std::find(bindings.begin(),bindings.end(), Binding(*args, queue)); + if (i != bindings.end()) bindings.erase(i); +} + + +void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args){ + Mutex::ScopedLock locker(lock);; + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (match(i->first, *args)) msg.deliverTo(i->second); + } +} + +HeadersExchange::~HeadersExchange() {} + +const std::string HeadersExchange::typeName("headers"); + +namespace +{ + + bool match_values(const Value& bind, const Value& msg) { + return dynamic_cast<const EmptyValue*>(&bind) || bind == msg; + } + +} + + +bool HeadersExchange::match(const FieldTable& bind, const FieldTable& msg) { + typedef FieldTable::ValueMap Map; + std::string what = bind.getString(x_match); + if (what == all) { + for (Map::const_iterator i = bind.getMap().begin(); + i != bind.getMap().end(); + ++i) + { + if (i->first != x_match) + { + Map::const_iterator j = msg.getMap().find(i->first); + if (j == msg.getMap().end()) return false; + if (!match_values(*(i->second), *(j->second))) return false; + } + } + return true; + } else if (what == any) { + for (Map::const_iterator i = bind.getMap().begin(); + i != bind.getMap().end(); + ++i) + { + if (i->first != x_match) + { + Map::const_iterator j = msg.getMap().find(i->first); + if (j != msg.getMap().end()) { + if (match_values(*(i->second), *(j->second))) return true; + } + } + } + return false; + } else { + return false; + } +} + + + diff --git a/cpp/lib/broker/HeadersExchange.h b/cpp/lib/broker/HeadersExchange.h new file mode 100644 index 0000000000..5e8da5ad85 --- /dev/null +++ b/cpp/lib/broker/HeadersExchange.h @@ -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. + * + */ +#ifndef _HeadersExchange_ +#define _HeadersExchange_ + +#include <vector> +#include <BrokerExchange.h> +#include <FieldTable.h> +#include <BrokerMessage.h> +#include <sys/Monitor.h> +#include <BrokerQueue.h> + +namespace qpid { +namespace broker { + + +class HeadersExchange : public virtual Exchange { + typedef std::pair<qpid::framing::FieldTable, Queue::shared_ptr> Binding; + typedef std::vector<Binding> Bindings; + + Bindings bindings; + qpid::sys::Mutex lock; + + public: + static const std::string typeName; + + HeadersExchange(const string& name); + + virtual std::string getType(){ return typeName; } + + virtual void bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual ~HeadersExchange(); + + static bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); +}; + + + +} +} + +#endif diff --git a/cpp/lib/broker/InMemoryContent.cpp b/cpp/lib/broker/InMemoryContent.cpp new file mode 100644 index 0000000000..3e4ac29486 --- /dev/null +++ b/cpp/lib/broker/InMemoryContent.cpp @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <InMemoryContent.h> +#include "AMQFrame.h" +#include "framing/ChannelAdapter.h" + +using namespace qpid::broker; +using namespace qpid::framing; +using boost::static_pointer_cast; + +void InMemoryContent::add(AMQContentBody::shared_ptr data) +{ + content.push_back(data); +} + +uint32_t InMemoryContent::size() +{ + int sum(0); + for (content_iterator i = content.begin(); i != content.end(); i++) { + sum += (*i)->size(); + } + return sum; +} + +// FIXME aconway 2007-02-01: Remove version parameter. +void InMemoryContent::send(ChannelAdapter& channel, uint32_t framesize) +{ + for (content_iterator i = content.begin(); i != content.end(); i++) { + if ((*i)->size() > framesize) { + uint32_t offset = 0; + for (int chunk = (*i)->size() / framesize; chunk > 0; chunk--) { + string data = (*i)->getData().substr(offset, framesize); + channel.send(new AMQContentBody(data)); + offset += framesize; + } + uint32_t remainder = (*i)->size() % framesize; + if (remainder) { + string data = (*i)->getData().substr(offset, remainder); + channel.send(new AMQContentBody(data)); + } + } else { + AMQBody::shared_ptr contentBody = + static_pointer_cast<AMQBody, AMQContentBody>(*i); + channel.send(contentBody); + } + } +} + +void InMemoryContent::encode(Buffer& buffer) +{ + for (content_iterator i = content.begin(); i != content.end(); i++) { + (*i)->encode(buffer); + } +} + diff --git a/cpp/lib/broker/InMemoryContent.h b/cpp/lib/broker/InMemoryContent.h new file mode 100644 index 0000000000..7a58ace3a7 --- /dev/null +++ b/cpp/lib/broker/InMemoryContent.h @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _InMemoryContent_ +#define _InMemoryContent_ + +#include <Content.h> +#include <vector> + + +namespace qpid { + namespace broker { + class InMemoryContent : public Content{ + typedef std::vector<qpid::framing::AMQContentBody::shared_ptr> content_list; + typedef content_list::iterator content_iterator; + + content_list content; + public: + void add(qpid::framing::AMQContentBody::shared_ptr data); + uint32_t size(); + void send(framing::ChannelAdapter&, uint32_t framesize); + void encode(qpid::framing::Buffer& buffer); + }; + } +} + + +#endif diff --git a/cpp/lib/broker/LazyLoadedContent.cpp b/cpp/lib/broker/LazyLoadedContent.cpp new file mode 100644 index 0000000000..131943b448 --- /dev/null +++ b/cpp/lib/broker/LazyLoadedContent.cpp @@ -0,0 +1,68 @@ +/* + * + * 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 <LazyLoadedContent.h> +#include "AMQFrame.h" +#include "framing/ChannelAdapter.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +LazyLoadedContent::~LazyLoadedContent() +{ + store->destroy(msg); +} + +LazyLoadedContent::LazyLoadedContent(MessageStore* const _store, Message* const _msg, uint64_t _expectedSize) : + store(_store), msg(_msg), expectedSize(_expectedSize) {} + +void LazyLoadedContent::add(AMQContentBody::shared_ptr data) +{ + store->appendContent(msg, data->getData()); +} + +uint32_t LazyLoadedContent::size() +{ + return 0;//all content is written as soon as it is added +} + +void LazyLoadedContent::send(ChannelAdapter& channel, uint32_t framesize) +{ + if (expectedSize > framesize) { + for (uint64_t offset = 0; offset < expectedSize; offset += framesize) + { + uint64_t remaining = expectedSize - offset; + string data; + store->loadContent(msg, data, offset, + remaining > framesize ? framesize : remaining); + channel.send(new AMQContentBody(data)); + } + } else { + string data; + store->loadContent(msg, data, 0, expectedSize); + channel.send(new AMQContentBody(data)); + } +} + +void LazyLoadedContent::encode(Buffer&) +{ + //do nothing as all content is written as soon as it is added +} + diff --git a/cpp/lib/broker/LazyLoadedContent.h b/cpp/lib/broker/LazyLoadedContent.h new file mode 100644 index 0000000000..e000a4ef69 --- /dev/null +++ b/cpp/lib/broker/LazyLoadedContent.h @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _LazyLoadedContent_ +#define _LazyLoadedContent_ + +#include <Content.h> +#include <MessageStore.h> + +namespace qpid { + namespace broker { + class LazyLoadedContent : public Content{ + MessageStore* const store; + Message* const msg; + const uint64_t expectedSize; + public: + LazyLoadedContent( + MessageStore* const store, Message* const msg, + uint64_t expectedSize); + ~LazyLoadedContent(); + void add(qpid::framing::AMQContentBody::shared_ptr data); + uint32_t size(); + void send( + framing::ChannelAdapter&, + uint32_t framesize); + void encode(qpid::framing::Buffer& buffer); + }; + } +} + + +#endif diff --git a/cpp/lib/broker/Makefile.am b/cpp/lib/broker/Makefile.am new file mode 100644 index 0000000000..68649c2b28 --- /dev/null +++ b/cpp/lib/broker/Makefile.am @@ -0,0 +1,96 @@ +AM_CXXFLAGS = $(WARNING_CFLAGS) +INCLUDES = \ + -I$(top_srcdir)/gen \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/common/sys \ + -I$(top_srcdir)/lib/common/framing \ + $(APR_CXXFLAGS) + +lib_LTLIBRARIES = libqpidbroker.la +libqpidbroker_la_LIBADD = ../common/libqpidcommon.la +libqpidbroker_la_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO_ARG) +libqpidbroker_la_SOURCES = \ + AccumulatedAck.cpp \ + AccumulatedAck.h \ + AutoDelete.cpp \ + AutoDelete.h \ + Binding.h \ + Broker.cpp \ + Broker.h \ + BrokerSingleton.cpp \ + BrokerSingleton.h \ + BrokerChannel.cpp \ + BrokerChannel.h \ + BrokerExchange.h \ + BrokerMessage.cpp \ + BrokerMessage.h \ + BrokerMessageMessage.cpp \ + BrokerMessageMessage.h \ + BrokerQueue.cpp \ + BrokerQueue.h \ + Configuration.cpp \ + Configuration.h \ + ConnectionToken.h \ + Consumer.h \ + Content.h \ + DeletingTxOp.cpp \ + DeletingTxOp.h \ + Deliverable.h \ + DeliverableMessage.cpp \ + DeliverableMessage.h \ + DeliveryRecord.cpp \ + DeliveryRecord.h \ + DirectExchange.cpp \ + DirectExchange.h \ + ExchangeBinding.cpp \ + ExchangeBinding.h \ + ExchangeRegistry.cpp \ + ExchangeRegistry.h \ + FanOutExchange.cpp \ + FanOutExchange.h \ + HeadersExchange.cpp \ + HeadersExchange.h \ + InMemoryContent.cpp \ + InMemoryContent.h \ + LazyLoadedContent.cpp \ + LazyLoadedContent.h \ + MessageBuilder.cpp \ + MessageBuilder.h \ + MessageStore.h \ + MessageStoreModule.cpp \ + MessageStoreModule.h \ + NameGenerator.cpp \ + NameGenerator.h \ + NullMessageStore.cpp \ + NullMessageStore.h \ + Prefetch.h \ + QueuePolicy.cpp \ + QueuePolicy.h \ + QueueRegistry.cpp \ + QueueRegistry.h \ + RecoveryManager.cpp \ + RecoveryManager.h \ + Reference.cpp \ + Reference.h \ + ConnectionFactory.cpp \ + ConnectionFactory.h \ + Connection.cpp \ + Connection.h \ + BrokerAdapter.cpp \ + BrokerAdapter.h \ + MessageHandlerImpl.cpp \ + MessageHandlerImpl.h \ + TopicExchange.cpp \ + TopicExchange.h \ + TransactionalStore.h \ + TxAck.cpp \ + TxAck.h \ + TxBuffer.cpp \ + TxBuffer.h \ + TxOp.h \ + TxPublish.cpp \ + TxPublish.h + + +# Force build during dist phase so help2man will work. +dist-hook: $(lib_LTLIBRARIES) diff --git a/cpp/lib/broker/MessageBuilder.cpp b/cpp/lib/broker/MessageBuilder.cpp new file mode 100644 index 0000000000..8bffaef50f --- /dev/null +++ b/cpp/lib/broker/MessageBuilder.cpp @@ -0,0 +1,74 @@ +/* + * + * 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 <MessageBuilder.h> + +#include <InMemoryContent.h> +#include <LazyLoadedContent.h> + +using namespace qpid::broker; +using namespace qpid::framing; +using std::auto_ptr; + +MessageBuilder::MessageBuilder(CompletionHandler* _handler, + MessageStore* const _store, + uint64_t _stagingThreshold +) : + handler(_handler), + store(_store), + stagingThreshold(_stagingThreshold) +{} + +void MessageBuilder::route(){ + if (message->isComplete()) { + if (handler) handler->complete(message); + message.reset(); + } +} + +void MessageBuilder::initialise(Message::shared_ptr& msg){ + if(message.get()){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got publish before previous content was completed."); + } + message = msg; +} + +void MessageBuilder::setHeader(AMQHeaderBody::shared_ptr& header){ + if(!message.get()){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got header before publish."); + } + message->setHeader(header); + if (stagingThreshold && header->getContentSize() >= stagingThreshold) { + store->stage(message.get()); + message->releaseContent(store); + } else { + auto_ptr<Content> content(new InMemoryContent()); + message->setContent(content); + } + route(); +} + +void MessageBuilder::addContent(AMQContentBody::shared_ptr& content){ + if(!message.get()){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got content before publish."); + } + message->addContent(content); + route(); +} diff --git a/cpp/lib/broker/MessageBuilder.h b/cpp/lib/broker/MessageBuilder.h new file mode 100644 index 0000000000..30834e1075 --- /dev/null +++ b/cpp/lib/broker/MessageBuilder.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _MessageBuilder_ +#define _MessageBuilder_ + +#include <memory> +#include <QpidError.h> +#include <BrokerExchange.h> +#include <BrokerMessage.h> +#include <MessageStore.h> +#include <AMQContentBody.h> +#include <AMQHeaderBody.h> +#include <BasicPublishBody.h> +#include "CompletionHandler.h" + +namespace qpid { + namespace broker { + class MessageBuilder{ + public: + MessageBuilder(CompletionHandler* _handler, + MessageStore* const store = 0, + uint64_t stagingThreshold = 0); + void initialise(Message::shared_ptr& msg); + void setHeader(framing::AMQHeaderBody::shared_ptr& header); + void addContent(framing::AMQContentBody::shared_ptr& content); + Message::shared_ptr getMessage() { return message; } + private: + Message::shared_ptr message; + CompletionHandler* handler; + MessageStore* const store; + const uint64_t stagingThreshold; + + void route(); + }; + } +} + + +#endif diff --git a/cpp/lib/broker/MessageHandlerImpl.cpp b/cpp/lib/broker/MessageHandlerImpl.cpp new file mode 100644 index 0000000000..fa7c10f26c --- /dev/null +++ b/cpp/lib/broker/MessageHandlerImpl.cpp @@ -0,0 +1,243 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "QpidError.h" +#include "MessageHandlerImpl.h" +#include "BrokerChannel.h" +#include "FramingContent.h" +#include "Connection.h" +#include "Broker.h" +#include "BrokerMessageMessage.h" +#include "MessageAppendBody.h" +#include "MessageTransferBody.h" +#include "BrokerAdapter.h" + +namespace qpid { +namespace broker { + +using namespace framing; + +MessageHandlerImpl::MessageHandlerImpl(CoreRefs& parent) + : HandlerImplType(parent) {} + +// +// Message class method handlers +// + +void +MessageHandlerImpl::cancel(const MethodContext& context, + const string& destination ) +{ + channel.cancel(destination); + client.ok(context.getRequestId()); +} + +void +MessageHandlerImpl::open(const MethodContext& context, + const string& reference) +{ + references.open(reference); + client.ok(context.getRequestId()); +} + +void +MessageHandlerImpl::append(const MethodContext& context, + const string& reference, + const string& /*bytes*/ ) +{ + references.get(reference)->append( + boost::shared_polymorphic_downcast<MessageAppendBody>( + context.methodBody)); + client.ok(context.getRequestId()); +} + +void +MessageHandlerImpl::close(const MethodContext& context, + const string& reference) +{ + Reference::shared_ptr ref = references.get(reference); + client.ok(context.getRequestId()); + + // Send any transfer messages to their correct exchanges and okay them + const Reference::Messages& msgs = ref->getMessages(); + for (Reference::Messages::const_iterator m = msgs.begin(); m != msgs.end(); ++m) { + channel.handleInlineTransfer(*m); + client.ok((*m)->getRequestId()); + } + ref->close(); +} + +void +MessageHandlerImpl::checkpoint(const MethodContext& context, + const string& /*reference*/, + const string& /*identifier*/ ) +{ + // Initial implementation (which is conforming) is to do nothing here + // and return offset zero for the resume + client.ok(context.getRequestId()); +} + +void +MessageHandlerImpl::resume(const MethodContext& context, + const string& reference, + const string& /*identifier*/ ) +{ + // Initial (null) implementation + // open reference and return 0 offset + references.open(reference); + client.offset(0, context.getRequestId()); +} + +void +MessageHandlerImpl::offset(const MethodContext&, + uint64_t /*value*/ ) +{ + // Shouldn't ever receive this as it is reponse to resume + // which is never sent + // TODO astitcher 2007-02-16 What is the correct exception to throw here? + THROW_QPID_ERROR(INTERNAL_ERROR, "impossible"); +} + +void +MessageHandlerImpl::consume(const MethodContext& context, + uint16_t /*ticket*/, + const string& queueName, + const string& destination, + bool noLocal, + bool noAck, + bool exclusive, + const framing::FieldTable& filter ) +{ + Queue::shared_ptr queue = connection.getQueue(queueName, channel.getId()); + if(!destination.empty() && channel.exists(destination)) + throw ConnectionException(530, "Consumer tags must be unique"); + string tag = destination; + channel.consume( + tag, queue, !noAck, exclusive, + noLocal ? &connection : 0, &filter); + client.ok(context.getRequestId()); + // Dispatch messages as there is now a consumer. + queue->dispatch(); +} + +void +MessageHandlerImpl::get( const MethodContext& context, + uint16_t /*ticket*/, + const string& queueName, + const string& destination, + bool noAck ) +{ + Queue::shared_ptr queue = + connection.getQueue(queueName, context.channel->getId()); + + if(channel.get(queue, destination, !noAck)) + client.ok(context.getRequestId()); + else + client.empty(context.getRequestId()); +} + +void +MessageHandlerImpl::empty( const MethodContext& ) +{ + // Shouldn't ever receive this as it is a response to get + // which is never sent + // TODO astitcher 2007-02-09 What is the correct exception to throw here? + THROW_QPID_ERROR(INTERNAL_ERROR, "Impossible"); +} + +void +MessageHandlerImpl::ok(const MethodContext& /*context*/) +{ + channel.ack(); +} + +void +MessageHandlerImpl::qos(const MethodContext& context, + uint32_t prefetchSize, + uint16_t prefetchCount, + bool /*global*/ ) +{ + //TODO: handle global + channel.setPrefetchSize(prefetchSize); + channel.setPrefetchCount(prefetchCount); + client.ok(context.getRequestId()); +} + +void +MessageHandlerImpl::recover(const MethodContext& context, + bool requeue) +{ + channel.recover(requeue); + client.ok(context.getRequestId()); +} + +void +MessageHandlerImpl::reject(const MethodContext& /*context*/, + uint16_t /*code*/, + const string& /*text*/ ) +{ + channel.ack(); + // channel.requeue(); +} + +void +MessageHandlerImpl::transfer(const MethodContext& context, + uint16_t /*ticket*/, + const string& /* destination */, + bool /*redelivered*/, + bool /*immediate*/, + uint64_t /*ttl*/, + uint8_t /*priority*/, + uint64_t /*timestamp*/, + uint8_t /*deliveryMode*/, + uint64_t /*expiration*/, + const string& /*exchangeName*/, + const string& /*routingKey*/, + const string& /*messageId*/, + const string& /*correlationId*/, + const string& /*replyTo*/, + const string& /*contentType*/, + const string& /*contentEncoding*/, + const string& /*userId*/, + const string& /*appId*/, + const string& /*transactionId*/, + const string& /*securityToken*/, + const framing::FieldTable& /*applicationHeaders*/, + const framing::Content& body, + bool /*mandatory*/) +{ + MessageTransferBody::shared_ptr transfer( + boost::shared_polymorphic_downcast<MessageTransferBody>( + context.methodBody)); + RequestId requestId = context.getRequestId(); + + if (body.isInline()) { + MessageMessage::shared_ptr message( + new MessageMessage(&connection, requestId, transfer)); + channel.handleInlineTransfer(message); + client.ok(requestId); + } else { + Reference::shared_ptr ref(references.get(body.getValue())); + MessageMessage::shared_ptr message( + new MessageMessage(&connection, requestId, transfer, ref)); + ref->addMessage(message); + } +} + + +}} // namespace qpid::broker diff --git a/cpp/lib/broker/MessageHandlerImpl.h b/cpp/lib/broker/MessageHandlerImpl.h new file mode 100644 index 0000000000..872d429d5c --- /dev/null +++ b/cpp/lib/broker/MessageHandlerImpl.h @@ -0,0 +1,130 @@ +#ifndef _broker_MessageHandlerImpl_h +#define _broker_MessageHandlerImpl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <memory> + +#include "AMQP_ServerOperations.h" +#include "AMQP_ClientProxy.h" +#include "Reference.h" +#include "HandlerImpl.h" + +namespace qpid { +namespace broker { + +class Connection; +class Broker; +class MessageMessage; + +class MessageHandlerImpl : + public framing::AMQP_ServerOperations::MessageHandler, + public HandlerImpl<framing::AMQP_ClientProxy::Message> +{ + public: + MessageHandlerImpl(CoreRefs& parent); + + void append(const framing::MethodContext&, + const std::string& reference, + const std::string& bytes ); + + void cancel(const framing::MethodContext&, + const std::string& destination ); + + void checkpoint(const framing::MethodContext&, + const std::string& reference, + const std::string& identifier ); + + void close(const framing::MethodContext&, + const std::string& reference ); + + void consume(const framing::MethodContext&, + uint16_t ticket, + const std::string& queue, + const std::string& destination, + bool noLocal, + bool noAck, + bool exclusive, + const framing::FieldTable& filter ); + + void empty( const framing::MethodContext& ); + + void get(const framing::MethodContext&, + uint16_t ticket, + const std::string& queue, + const std::string& destination, + bool noAck ); + + void offset(const framing::MethodContext&, + uint64_t value ); + + void ok( const framing::MethodContext& ); + + void open(const framing::MethodContext&, + const std::string& reference ); + + void qos(const framing::MethodContext&, + uint32_t prefetchSize, + uint16_t prefetchCount, + bool global ); + + void recover(const framing::MethodContext&, + bool requeue ); + + void reject(const framing::MethodContext&, + uint16_t code, + const std::string& text ); + + void resume(const framing::MethodContext&, + const std::string& reference, + const std::string& identifier ); + + void transfer(const framing::MethodContext&, + uint16_t ticket, + const std::string& destination, + bool redelivered, + bool immediate, + uint64_t ttl, + uint8_t priority, + uint64_t timestamp, + uint8_t deliveryMode, + uint64_t expiration, + const std::string& exchange, + const std::string& routingKey, + const std::string& messageId, + const std::string& correlationId, + const std::string& replyTo, + const std::string& contentType, + const std::string& contentEncoding, + const std::string& userId, + const std::string& appId, + const std::string& transactionId, + const std::string& securityToken, + const framing::FieldTable& applicationHeaders, + const framing::Content& body, + bool mandatory ); + private: + ReferenceRegistry references; +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_MessageHandlerImpl_h*/ diff --git a/cpp/lib/broker/MessageStore.h b/cpp/lib/broker/MessageStore.h new file mode 100644 index 0000000000..9e38408886 --- /dev/null +++ b/cpp/lib/broker/MessageStore.h @@ -0,0 +1,140 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _MessageStore_ +#define _MessageStore_ + +#include <BrokerMessage.h> +#include <FieldTable.h> +#include <RecoveryManager.h> +#include <TransactionalStore.h> + +namespace qpid { + namespace broker { + struct MessageStoreSettings + { + /** + * Messages whose content length is larger than this value + * will be staged (i.e. will have thier data written to + * disk as it arrives) and will load their data lazily. On + * recovery therefore, only the headers should be loaded. + */ + uint64_t stagingThreshold; + }; + /** + * An abstraction of the persistent storage for messages. (In + * all methods, any pointers/references to queues or messages + * are valid only for the duration of the call). + */ + class MessageStore : public TransactionalStore{ + public: + /** + * Record the existance of a durable queue + */ + virtual void create(const Queue& queue, const qpid::framing::FieldTable& settings) = 0; + /** + * Destroy a durable queue + */ + virtual void destroy(const Queue& queue) = 0; + + /** + * Request recovery of queue and message state from store + */ + virtual void recover(RecoveryManager& queues, const MessageStoreSettings* const settings = 0) = 0; + + /** + * Stores a messages before it has been enqueued + * (enqueueing automatically stores the message so this is + * only required if storage is required prior to that + * point). If the message has not yet been stored it will + * store the headers as well as any content passed in. A + * persistence id will be set on the message which can be + * used to load the content or to append to it. + */ + virtual void stage(Message* const msg) = 0; + + /** + * Destroys a previously staged message. This only needs + * to be called if the message is never enqueued. (Once + * enqueued, deletion will be automatic when the message + * is dequeued from all queues it was enqueued onto). + */ + virtual void destroy(Message* const msg) = 0; + + /** + * Appends content to a previously staged message + */ + virtual void appendContent(Message* const msg, const std::string& data) = 0; + + /** + * Loads (a section) of content data for the specified + * message (previously stored through a call to stage or + * enqueue) into data. The offset refers to the content + * only (i.e. an offset of 0 implies that the start of the + * content should be loaded, not the headers or related + * meta-data). + */ + virtual void loadContent(Message* const msg, std::string& data, uint64_t offset, uint32_t length) = 0; + + /** + * Enqueues a message, storing the message if it has not + * been previously stored and recording that the given + * message is on the given queue. + * + * @param msg the message to enqueue + * @param queue the name of the queue onto which it is to be enqueued + * @param xid (a pointer to) an identifier of the + * distributed transaction in which the operation takes + * place or null for 'local' transactions + */ + virtual void enqueue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const std::string * const xid) = 0; + /** + * Dequeues a message, recording that the given message is + * no longer on the given queue and deleting the message + * if it is no longer on any other queue. + * + * @param msg the message to dequeue + * @param queue the name of th queue from which it is to be dequeued + * @param xid (a pointer to) an identifier of the + * distributed transaction in which the operation takes + * place or null for 'local' transactions + */ + virtual void dequeue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const std::string * const xid) = 0; + + /** + * Treat all enqueue/dequeues where this xid was specified as being prepared. + */ + virtual void prepared(const std::string * const xid) = 0; + /** + * Treat all enqueue/dequeues where this xid was specified as being committed. + */ + virtual void committed(const std::string * const xid) = 0; + /** + * Treat all enqueue/dequeues where this xid was specified as being aborted. + */ + virtual void aborted(const std::string * const xid) = 0; + + virtual ~MessageStore(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/MessageStoreModule.cpp b/cpp/lib/broker/MessageStoreModule.cpp new file mode 100644 index 0000000000..676e86f84a --- /dev/null +++ b/cpp/lib/broker/MessageStoreModule.cpp @@ -0,0 +1,104 @@ +/* + * + * 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 <MessageStoreModule.h> +#include <iostream> + +using namespace qpid::broker; + +MessageStoreModule::MessageStoreModule(const std::string& name) : store(name) +{ +} + +void MessageStoreModule::create(const Queue& queue, const qpid::framing::FieldTable& settings) +{ + store->create(queue, settings); +} + +void MessageStoreModule::destroy(const Queue& queue) +{ + store->destroy(queue); +} + +void MessageStoreModule::recover(RecoveryManager& registry, const MessageStoreSettings* const settings) +{ + store->recover(registry, settings); +} + +void MessageStoreModule::stage(Message* const msg) +{ + store->stage(msg); +} + +void MessageStoreModule::destroy(Message* const msg) +{ + store->destroy(msg); +} + +void MessageStoreModule::appendContent(Message* const msg, const std::string& data) +{ + store->appendContent(msg, data); +} + +void MessageStoreModule::loadContent(Message* const msg, string& data, uint64_t offset, uint32_t length) +{ + store->loadContent(msg, data, offset, length); +} + +void MessageStoreModule::enqueue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const string * const xid) +{ + store->enqueue(ctxt, msg, queue, xid); +} + +void MessageStoreModule::dequeue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const string * const xid) +{ + store->dequeue(ctxt, msg, queue, xid); +} + +void MessageStoreModule::prepared(const string * const xid) +{ + store->prepared(xid); +} + +void MessageStoreModule::committed(const string * const xid) +{ + store->committed(xid); +} + +void MessageStoreModule::aborted(const string * const xid) +{ + store->aborted(xid); +} + +std::auto_ptr<TransactionContext> MessageStoreModule::begin() +{ + return store->begin(); +} + +void MessageStoreModule::commit(TransactionContext* ctxt) +{ + store->commit(ctxt); +} + +void MessageStoreModule::abort(TransactionContext* ctxt) +{ + store->abort(ctxt); +} diff --git a/cpp/lib/broker/MessageStoreModule.h b/cpp/lib/broker/MessageStoreModule.h new file mode 100644 index 0000000000..27fedbf635 --- /dev/null +++ b/cpp/lib/broker/MessageStoreModule.h @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _MessageStoreModule_ +#define _MessageStoreModule_ + +#include <BrokerMessage.h> +#include <MessageStore.h> +#include <BrokerQueue.h> +#include <RecoveryManager.h> +#include <sys/Module.h> + +namespace qpid { + namespace broker { + /** + * A null implementation of the MessageStore interface + */ + class MessageStoreModule : public MessageStore{ + qpid::sys::Module<MessageStore> store; + public: + MessageStoreModule(const std::string& name); + void create(const Queue& queue, const qpid::framing::FieldTable& settings); + void destroy(const Queue& queue); + void recover(RecoveryManager& queues, const MessageStoreSettings* const settings = 0); + void stage(Message* const msg); + void destroy(Message* const msg); + void appendContent(Message* const msg, const std::string& data); + void loadContent(Message* const msg, std::string& data, uint64_t offset, uint32_t length); + void enqueue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const string * const xid); + void dequeue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const string * const xid); + void prepared(const std::string * const xid); + void committed(const std::string * const xid); + void aborted(const std::string * const xid); + std::auto_ptr<TransactionContext> begin(); + void commit(TransactionContext* ctxt); + void abort(TransactionContext* ctxt); + ~MessageStoreModule(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/NameGenerator.cpp b/cpp/lib/broker/NameGenerator.cpp new file mode 100644 index 0000000000..3f281859fa --- /dev/null +++ b/cpp/lib/broker/NameGenerator.cpp @@ -0,0 +1,32 @@ +/* + * + * 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 <NameGenerator.h> +#include <sstream> + +using namespace qpid::broker; + +NameGenerator::NameGenerator(const std::string& _base) : base(_base), counter(1) {} + +std::string NameGenerator::generate(){ + std::stringstream ss; + ss << base << counter++; + return ss.str(); +} diff --git a/cpp/lib/broker/NameGenerator.h b/cpp/lib/broker/NameGenerator.h new file mode 100644 index 0000000000..b2dbbdfb69 --- /dev/null +++ b/cpp/lib/broker/NameGenerator.h @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _NameGenerator_ +#define _NameGenerator_ + +#include <BrokerMessage.h> + +namespace qpid { + namespace broker { + class NameGenerator{ + const std::string base; + unsigned int counter; + public: + NameGenerator(const std::string& base); + std::string generate(); + }; + } +} + + +#endif diff --git a/cpp/lib/broker/NullMessageStore.cpp b/cpp/lib/broker/NullMessageStore.cpp new file mode 100644 index 0000000000..bcb15c2ae0 --- /dev/null +++ b/cpp/lib/broker/NullMessageStore.cpp @@ -0,0 +1,104 @@ +/* + * + * 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 <NullMessageStore.h> + +#include <BrokerQueue.h> +#include <RecoveryManager.h> + +#include <iostream> + +using namespace qpid::broker; + +NullMessageStore::NullMessageStore(bool _warn) : warn(_warn){} + +void NullMessageStore::create(const Queue& queue, const qpid::framing::FieldTable&) +{ + if (warn) std::cout << "WARNING: Can't create durable queue '" << queue.getName() << "'. Persistence not enabled." << std::endl; +} + +void NullMessageStore::destroy(const Queue& queue) +{ + if (warn) std::cout << "WARNING: Can't destroy durable queue '" << queue.getName() << "'. Persistence not enabled." << std::endl; +} + +void NullMessageStore::recover(RecoveryManager&, const MessageStoreSettings* const) +{ + if (warn) std::cout << "WARNING: Persistence not enabled, no recovery of queues or messages." << std::endl; +} + +void NullMessageStore::stage(Message* const) +{ + if (warn) std::cout << "WARNING: Can't stage message. Persistence not enabled." << std::endl; +} + +void NullMessageStore::destroy(Message* const) +{ + if (warn) std::cout << "WARNING: No need to destroy staged message. Persistence not enabled." << std::endl; +} + +void NullMessageStore::appendContent(Message* const, const string&) +{ + if (warn) std::cout << "WARNING: Can't append content. Persistence not enabled." << std::endl; +} + +void NullMessageStore::loadContent(Message* const, string&, uint64_t, uint32_t) +{ + if (warn) std::cout << "WARNING: Can't load content. Persistence not enabled." << std::endl; +} + +void NullMessageStore::enqueue(TransactionContext*, Message* const, const Queue& queue, const string * const) +{ + if (warn) std::cout << "WARNING: Can't enqueue message onto '" << queue.getName() << "'. Persistence not enabled." << std::endl; +} + +void NullMessageStore::dequeue(TransactionContext*, Message* const, const Queue& queue, const string * const) +{ + if (warn) std::cout << "WARNING: Can't dequeue message from '" << queue.getName() << "'. Persistence not enabled." << std::endl; +} + +void NullMessageStore::prepared(const string * const) +{ + if (warn) std::cout << "WARNING: Persistence not enabled." << std::endl; +} + +void NullMessageStore::committed(const string * const) +{ + if (warn) std::cout << "WARNING: Persistence not enabled." << std::endl; +} + +void NullMessageStore::aborted(const string * const) +{ + if (warn) std::cout << "WARNING: Persistence not enabled." << std::endl; +} + +std::auto_ptr<TransactionContext> NullMessageStore::begin() +{ + return std::auto_ptr<TransactionContext>(); +} + +void NullMessageStore::commit(TransactionContext*) +{ +} + +void NullMessageStore::abort(TransactionContext*) +{ +} diff --git a/cpp/lib/broker/NullMessageStore.h b/cpp/lib/broker/NullMessageStore.h new file mode 100644 index 0000000000..705f18ab43 --- /dev/null +++ b/cpp/lib/broker/NullMessageStore.h @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _NullMessageStore_ +#define _NullMessageStore_ + +#include <BrokerMessage.h> +#include <MessageStore.h> +#include <BrokerQueue.h> + +namespace qpid { + namespace broker { + + /** + * A null implementation of the MessageStore interface + */ + class NullMessageStore : public MessageStore{ + const bool warn; + public: + NullMessageStore(bool warn = false); + virtual void create(const Queue& queue, const qpid::framing::FieldTable& settings); + virtual void destroy(const Queue& queue); + virtual void recover(RecoveryManager& queues, const MessageStoreSettings* const settings = 0); + virtual void stage(Message* const msg); + virtual void destroy(Message* const msg); + virtual void appendContent(Message* const msg, const std::string& data); + virtual void loadContent(Message* const msg, std::string& data, uint64_t offset, uint32_t length); + virtual void enqueue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const string * const xid); + virtual void dequeue(TransactionContext* ctxt, Message* const msg, const Queue& queue, const string * const xid); + virtual void prepared(const std::string * const xid); + virtual void committed(const std::string * const xid); + virtual void aborted(const std::string * const xid); + virtual std::auto_ptr<TransactionContext> begin(); + virtual void commit(TransactionContext* ctxt); + virtual void abort(TransactionContext* ctxt); + ~NullMessageStore(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/Prefetch.h b/cpp/lib/broker/Prefetch.h new file mode 100644 index 0000000000..b6d4026c3f --- /dev/null +++ b/cpp/lib/broker/Prefetch.h @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _Prefetch_ +#define _Prefetch_ + +#include <amqp_types.h> + +namespace qpid { + namespace broker { + /** + * Count and total size of asynchronously delivered + * (i.e. pushed) messages that have acks outstanding. + */ + struct Prefetch{ + uint32_t size; + uint16_t count; + + void reset() { size = 0; count = 0; } + }; + } +} + + +#endif diff --git a/cpp/lib/broker/QueuePolicy.cpp b/cpp/lib/broker/QueuePolicy.cpp new file mode 100644 index 0000000000..94b86f2bbb --- /dev/null +++ b/cpp/lib/broker/QueuePolicy.cpp @@ -0,0 +1,69 @@ +/* + * + * 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 <QueuePolicy.h> + +using namespace qpid::broker; +using namespace qpid::framing; + +QueuePolicy::QueuePolicy(uint32_t _maxCount, uint64_t _maxSize) : + maxCount(_maxCount), maxSize(_maxSize), count(0), size(0) {} + +QueuePolicy::QueuePolicy(const FieldTable& settings) : + maxCount(getInt(settings, maxCountKey, 0)), + maxSize(getInt(settings, maxSizeKey, 0)), count(0), size(0) {} + +void QueuePolicy::enqueued(uint64_t _size) +{ + if (maxCount) count++; + if (maxSize) size += _size; +} + +void QueuePolicy::dequeued(uint64_t _size) +{ + if (maxCount) count--; + if (maxSize) size -= _size; +} + +bool QueuePolicy::limitExceeded() +{ + return (maxSize && size > maxSize) || (maxCount && count > maxCount); +} + +void QueuePolicy::update(FieldTable& settings) +{ + if (maxCount) settings.setInt(maxCountKey, maxCount); + if (maxSize) settings.setInt(maxSizeKey, maxSize); +} + + +int QueuePolicy::getInt(const FieldTable& settings, const std::string& key, int defaultValue) +{ + //Note: currently field table only contain signed 32 bit ints, which + // restricts the values that can be set on the queue policy. + try { + return settings.getInt(key); + } catch (FieldNotFoundException& ignore) { + return defaultValue; + } +} + +const std::string QueuePolicy::maxCountKey("qpid.max_count"); +const std::string QueuePolicy::maxSizeKey("qpid.max_size"); diff --git a/cpp/lib/broker/QueuePolicy.h b/cpp/lib/broker/QueuePolicy.h new file mode 100644 index 0000000000..e7688f3e67 --- /dev/null +++ b/cpp/lib/broker/QueuePolicy.h @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QueuePolicy_ +#define _QueuePolicy_ + +#include <FieldTable.h> + +namespace qpid { + namespace broker { + class QueuePolicy + { + static const std::string maxCountKey; + static const std::string maxSizeKey; + + const uint32_t maxCount; + const uint64_t maxSize; + uint32_t count; + uint64_t size; + + static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue); + + public: + QueuePolicy(uint32_t maxCount, uint64_t maxSize); + QueuePolicy(const qpid::framing::FieldTable& settings); + void enqueued(uint64_t size); + void dequeued(uint64_t size); + void update(qpid::framing::FieldTable& settings); + bool limitExceeded(); + uint32_t getMaxCount() const { return maxCount; } + uint64_t getMaxSize() const { return maxSize; } + }; + } +} + + +#endif diff --git a/cpp/lib/broker/QueueRegistry.cpp b/cpp/lib/broker/QueueRegistry.cpp new file mode 100644 index 0000000000..d33cd09840 --- /dev/null +++ b/cpp/lib/broker/QueueRegistry.cpp @@ -0,0 +1,78 @@ +/* + * + * 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 <QueueRegistry.h> +#include <sstream> +#include <assert.h> + +using namespace qpid::broker; +using namespace qpid::sys; + +QueueRegistry::QueueRegistry(MessageStore* const _store) : counter(1), store(_store){} + +QueueRegistry::~QueueRegistry(){} + +std::pair<Queue::shared_ptr, bool> +QueueRegistry::declare(const string& declareName, bool durable, + uint32_t autoDelete, const ConnectionToken* owner) +{ + Mutex::ScopedLock locker(lock); + string name = declareName.empty() ? generateName() : declareName; + assert(!name.empty()); + QueueMap::iterator i = queues.find(name); + if (i == queues.end()) { + Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner)); + queues[name] = queue; + return std::pair<Queue::shared_ptr, bool>(queue, true); + } else { + return std::pair<Queue::shared_ptr, bool>(i->second, false); + } +} + +void QueueRegistry::destroy(const string& name){ + Mutex::ScopedLock locker(lock); + queues.erase(name); +} + +Queue::shared_ptr QueueRegistry::find(const string& name){ + Mutex::ScopedLock locker(lock); + QueueMap::iterator i = queues.find(name); + if (i == queues.end()) { + return Queue::shared_ptr(); + } else { + return i->second; + } +} + +string QueueRegistry::generateName(){ + string name; + do { + std::stringstream ss; + ss << "tmp_" << counter++; + name = ss.str(); + // Thread safety: Private function, only called with lock held + // so this is OK. + } while(queues.find(name) != queues.end()); + return name; +} + +MessageStore* const QueueRegistry::getStore() const { + return store; +} diff --git a/cpp/lib/broker/QueueRegistry.h b/cpp/lib/broker/QueueRegistry.h new file mode 100644 index 0000000000..079034359e --- /dev/null +++ b/cpp/lib/broker/QueueRegistry.h @@ -0,0 +1,96 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _QueueRegistry_ +#define _QueueRegistry_ + +#include <map> +#include <sys/Monitor.h> +#include <BrokerQueue.h> + +namespace qpid { +namespace broker { + +/** + * A registry of queues indexed by queue name. + * + * Queues are reference counted using shared_ptr to ensure that they + * are deleted when and only when they are no longer in use. + * + */ +class QueueRegistry{ + + public: + QueueRegistry(MessageStore* const store = 0); + ~QueueRegistry(); + + /** + * Declare a queue. + * + * @return The queue and a boolean flag which is true if the queue + * was created by this declare call false if it already existed. + */ + std::pair<Queue::shared_ptr, bool> declare(const string& name, bool durable = false, uint32_t autodelete = 0, + const ConnectionToken* const owner = 0); + + /** + * Destroy the named queue. + * + * Note: if the queue is in use it is not actually destroyed until + * all shared_ptrs to it are destroyed. During that time it is + * possible that a new queue with the same name may be + * created. This should not create any problems as the new and + * old queues exist independently. The registry has + * forgotten the old queue so there can be no confusion for + * subsequent calls to find or declare with the same name. + * + */ + void destroy(const string& name); + + /** + * Find the named queue. Return 0 if not found. + */ + Queue::shared_ptr find(const string& name); + + /** + * Generate unique queue name. + */ + string generateName(); + + /** + * Return the message store used. + */ + MessageStore* const getStore() const; + + + private: + typedef std::map<string, Queue::shared_ptr> QueueMap; + QueueMap queues; + qpid::sys::Mutex lock; + int counter; + MessageStore* const store; +}; + + +} +} + + +#endif diff --git a/cpp/lib/broker/RecoveryManager.cpp b/cpp/lib/broker/RecoveryManager.cpp new file mode 100644 index 0000000000..6548e6a24f --- /dev/null +++ b/cpp/lib/broker/RecoveryManager.cpp @@ -0,0 +1,46 @@ +/* + * + * 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 <RecoveryManager.h> + +using namespace qpid::broker; + +RecoveryManager::RecoveryManager(QueueRegistry& _queues, ExchangeRegistry& _exchanges) : queues(_queues), exchanges(_exchanges) {} + +RecoveryManager::~RecoveryManager() {} + +Queue::shared_ptr RecoveryManager::recoverQueue(const string& name) +{ + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true); + try { + Exchange::shared_ptr exchange = exchanges.getDefault(); + if (exchange) { + exchange->bind(result.first, result.first->getName(), 0); + } + } catch (ChannelException& e) { + //assume no default exchange has been declared + } + return result.first; +} + +Exchange::shared_ptr RecoveryManager::recoverExchange(const string& name, const string& type) +{ + return exchanges.declare(name, type).first; +} diff --git a/cpp/lib/broker/RecoveryManager.h b/cpp/lib/broker/RecoveryManager.h new file mode 100644 index 0000000000..d4e4cff3fd --- /dev/null +++ b/cpp/lib/broker/RecoveryManager.h @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _RecoveryManager_ +#define _RecoveryManager_ + +#include <ExchangeRegistry.h> +#include <QueueRegistry.h> + +namespace qpid { +namespace broker { + + class RecoveryManager{ + QueueRegistry& queues; + ExchangeRegistry& exchanges; + public: + RecoveryManager(QueueRegistry& queues, ExchangeRegistry& exchanges); + ~RecoveryManager(); + Queue::shared_ptr recoverQueue(const std::string& name); + Exchange::shared_ptr recoverExchange(const std::string& name, const std::string& type); + }; + + +} +} + + +#endif diff --git a/cpp/lib/broker/Reference.cpp b/cpp/lib/broker/Reference.cpp new file mode 100644 index 0000000000..c4c33e6363 --- /dev/null +++ b/cpp/lib/broker/Reference.cpp @@ -0,0 +1,54 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/bind.hpp> +#include "Reference.h" +#include "BrokerMessageMessage.h" +#include "QpidError.h" +#include "MessageAppendBody.h" +#include "CompletionHandler.h" + +namespace qpid { +namespace broker { + +Reference::shared_ptr ReferenceRegistry::open(const Reference::Id& id) { + ReferenceMap::iterator i = references.find(id); + // TODO aconway 2007-02-05: should we throw Channel or Connection + // exceptions here? + if (i != references.end()) + throw ConnectionException(503, "Attempt to re-open reference " +id); + return references[id] = Reference::shared_ptr(new Reference(id, this)); +} + +Reference::shared_ptr ReferenceRegistry::get(const Reference::Id& id) { + ReferenceMap::iterator i = references.find(id); + if (i == references.end()) + throw ConnectionException(503, "Attempt to use non-existent reference "+id); + return i->second; +} + +void Reference::append(AppendPtr ptr) { + appends.push_back(ptr); + size += ptr->getBytes().length(); +} + +void Reference::close() { + registry->references.erase(getId()); +} + +}} // namespace qpid::broker diff --git a/cpp/lib/broker/Reference.h b/cpp/lib/broker/Reference.h new file mode 100644 index 0000000000..e453645a54 --- /dev/null +++ b/cpp/lib/broker/Reference.h @@ -0,0 +1,111 @@ +#ifndef _broker_Reference_h +#define _broker_Reference_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <string> +#include <vector> +#include <map> +#include <boost/shared_ptr.hpp> +#include <boost/range.hpp> + +namespace qpid { + +namespace framing { +class MessageAppendBody; +} + +namespace broker { + +class MessageMessage; +class ReferenceRegistry; + +/** + * A reference is an accumulation point for data in a multi-frame + * message. A reference can be used by multiple transfer commands to + * create multiple messages, so the reference tracks which commands + * are using it. When the reference is closed, all the associated + * transfers are completed. + * + * THREAD UNSAFE: per-channel resource, access to channels is + * serialized. + */ +class Reference +{ + public: + typedef std::string Id; + typedef boost::shared_ptr<Reference> shared_ptr; + typedef boost::shared_ptr<MessageMessage> MessagePtr; + typedef std::vector<MessagePtr> Messages; + typedef boost::shared_ptr<framing::MessageAppendBody> AppendPtr; + typedef std::vector<AppendPtr> Appends; + + Reference(const Id& id_=Id(), ReferenceRegistry* reg=0) + : id(id_), size(0), registry(reg) {} + + const std::string& getId() const { return id; } + uint64_t getSize() const { return size; } + + /** Add a message to be completed with this reference */ + void addMessage(MessagePtr message) { messages.push_back(message); } + + /** Append more data to the reference */ + void append(AppendPtr ptr); + + /** Close the reference, complete each associated message */ + void close(); + + const Appends& getAppends() const { return appends; } + const Messages& getMessages() const { return messages; } + + private: + Id id; + uint64_t size; + ReferenceRegistry* registry; + Messages messages; + Appends appends; +}; + + +/** + * A registry/factory for references. + * + * THREAD UNSAFE: per-channel resource, access to channels is + * serialized. + */ +class ReferenceRegistry { + public: + ReferenceRegistry() {}; + Reference::shared_ptr open(const Reference::Id& id); + Reference::shared_ptr get(const Reference::Id& id); + + private: + typedef std::map<Reference::Id, Reference::shared_ptr> ReferenceMap; + ReferenceMap references; + + // Reference calls references.erase(). + friend class Reference; +}; + + +}} // namespace qpid::broker + + + +#endif /*!_broker_Reference_h*/ diff --git a/cpp/lib/broker/TopicExchange.cpp b/cpp/lib/broker/TopicExchange.cpp new file mode 100644 index 0000000000..3ebb3c8c56 --- /dev/null +++ b/cpp/lib/broker/TopicExchange.cpp @@ -0,0 +1,156 @@ +/* + * + * 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 <TopicExchange.h> +#include <ExchangeBinding.h> +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +// TODO aconway 2006-09-20: More efficient matching algorithm. +// Areas for improvement: +// - excessive string copying: should be 0 copy, match from original buffer. +// - match/lookup: use descision tree or other more efficient structure. + +Tokens& Tokens::operator=(const std::string& s) { + clear(); + if (s.empty()) return *this; + std::string::const_iterator i = s.begin(); + while (true) { + // Invariant: i is at the beginning of the next untokenized word. + std::string::const_iterator j = find(i, s.end(), '.'); + push_back(std::string(i, j)); + if (j == s.end()) return *this; + i = j + 1; + } + return *this; +} + +TopicPattern& TopicPattern::operator=(const Tokens& tokens) { + Tokens::operator=(tokens); + normalize(); + return *this; +} + +namespace { +const std::string hashmark("#"); +const std::string star("*"); +} + +void TopicPattern::normalize() { + std::string word; + Tokens::iterator i = begin(); + while (i != end()) { + if (*i == hashmark) { + ++i; + while (i != end()) { + // Invariant: *(i-1)==#, [begin()..i-1] is normalized. + if (*i == star) { // Move * before #. + std::swap(*i, *(i-1)); + ++i; + } else if (*i == hashmark) { + erase(i); // Remove extra # + } else { + break; + } + } + } else { + i ++; + } + } +} + + +namespace { +// TODO aconway 2006-09-20: Ineficient to convert every routingKey to a string. +// Need StringRef class that operates on a string in place witout copy. +// Should be applied everywhere strings are extracted from frames. +// +bool do_match(Tokens::const_iterator pattern_begin, Tokens::const_iterator pattern_end, Tokens::const_iterator target_begin, Tokens::const_iterator target_end) +{ + // Invariant: [pattern_begin..p) matches [target_begin..t) + Tokens::const_iterator p = pattern_begin; + Tokens::const_iterator t = target_begin; + while (p != pattern_end && t != target_end) + { + if (*p == star || *p == *t) { + ++p, ++t; + } else if (*p == hashmark) { + ++p; + if (do_match(p, pattern_end, t, target_end)) return true; + while (t != target_end) { + ++t; + if (do_match(p, pattern_end, t, target_end)) return true; + } + return false; + } else { + return false; + } + } + while (p != pattern_end && *p == hashmark) ++p; // Ignore trailing # + return t == target_end && p == pattern_end; +} +} + +bool TopicPattern::match(const Tokens& target) const +{ + return do_match(begin(), end(), target.begin(), target.end()); +} + +TopicExchange::TopicExchange(const string& _name) : Exchange(_name) { } + +void TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args){ + Monitor::ScopedLock l(lock); + TopicPattern routingPattern(routingKey); + bindings[routingPattern].push_back(queue); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); +} + +void TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ + Monitor::ScopedLock l(lock); + BindingMap::iterator bi = bindings.find(TopicPattern(routingKey)); + Queue::vector& qv(bi->second); + if (bi == bindings.end()) return; + Queue::vector::iterator q = find(qv.begin(), qv.end(), queue); + if(q == qv.end()) return; + qv.erase(q); + if(qv.empty()) bindings.erase(bi); +} + + +void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ + Monitor::ScopedLock l(lock); + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (i->first.match(routingKey)) { + Queue::vector& qv(i->second); + for(Queue::vector::iterator j = qv.begin(); j != qv.end(); j++){ + msg.deliverTo(*j); + } + } + } +} + +TopicExchange::~TopicExchange() {} + +const std::string TopicExchange::typeName("topic"); + + diff --git a/cpp/lib/broker/TopicExchange.h b/cpp/lib/broker/TopicExchange.h new file mode 100644 index 0000000000..fa0c86863a --- /dev/null +++ b/cpp/lib/broker/TopicExchange.h @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TopicExchange_ +#define _TopicExchange_ + +#include <map> +#include <vector> +#include <BrokerExchange.h> +#include <FieldTable.h> +#include <BrokerMessage.h> +#include <sys/Monitor.h> +#include <BrokerQueue.h> + +namespace qpid { +namespace broker { + +/** A vector of string tokens */ +class Tokens : public std::vector<std::string> { + public: + Tokens() {}; + // Default copy, assign, dtor are sufficient. + + /** Tokenize s, provides automatic conversion of string to Tokens */ + Tokens(const std::string& s) { operator=(s); } + /** Tokenizing assignment operator s */ + Tokens & operator=(const std::string& s); + + private: + size_t hash; +}; + + +/** + * Tokens that have been normalized as a pattern and can be matched + * with topic Tokens. Normalized meands all sequences of mixed * and + * # are reduced to a series of * followed by at most one #. + */ +class TopicPattern : public Tokens +{ + public: + TopicPattern() {} + // Default copy, assign, dtor are sufficient. + TopicPattern(const Tokens& tokens) { operator=(tokens); } + TopicPattern(const std::string& str) { operator=(str); } + TopicPattern& operator=(const Tokens&); + TopicPattern& operator=(const std::string& str) { return operator=(Tokens(str)); } + + /** Match a topic */ + bool match(const std::string& topic) { return match(Tokens(topic)); } + bool match(const Tokens& topic) const; + + private: + void normalize(); +}; + +class TopicExchange : public virtual Exchange{ + typedef std::map<TopicPattern, Queue::vector> BindingMap; + BindingMap bindings; + qpid::sys::Mutex lock; + + public: + static const std::string typeName; + + TopicExchange(const string& name); + + virtual std::string getType(){ return typeName; } + + virtual void bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual ~TopicExchange(); +}; + + + +} +} + +#endif diff --git a/cpp/lib/broker/TransactionalStore.h b/cpp/lib/broker/TransactionalStore.h new file mode 100644 index 0000000000..17bca3878a --- /dev/null +++ b/cpp/lib/broker/TransactionalStore.h @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TransactionalStore_ +#define _TransactionalStore_ + +#include <memory> + +namespace qpid { + namespace broker { + struct InvalidTransactionContextException : public std::exception {}; + + class TransactionContext{ + public: + virtual ~TransactionContext(){} + }; + + class TransactionalStore{ + public: + virtual std::auto_ptr<TransactionContext> begin() = 0; + virtual void commit(TransactionContext*) = 0; + virtual void abort(TransactionContext*) = 0; + + virtual ~TransactionalStore(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/TxAck.cpp b/cpp/lib/broker/TxAck.cpp new file mode 100644 index 0000000000..b5211158f3 --- /dev/null +++ b/cpp/lib/broker/TxAck.cpp @@ -0,0 +1,54 @@ +/* + * + * 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 <TxAck.h> + +using std::bind1st; +using std::bind2nd; +using std::mem_fun_ref; +using namespace qpid::broker; + +TxAck::TxAck(AccumulatedAck& _acked, std::list<DeliveryRecord>& _unacked, const std::string* const _xid) : + acked(_acked), unacked(_unacked), xid(_xid){ + +} + +bool TxAck::prepare(TransactionContext* ctxt) throw(){ + try{ + //dequeue all acked messages from their queues + for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) { + if (i->coveredBy(&acked)) { + i->discard(ctxt, xid); + } + } + return true; + }catch(...){ + std::cout << "TxAck::prepare() - Failed to prepare" << std::endl; + return false; + } +} + +void TxAck::commit() throw(){ + //remove all acked records from the list + unacked.remove_if(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked)); +} + +void TxAck::rollback() throw(){ +} diff --git a/cpp/lib/broker/TxAck.h b/cpp/lib/broker/TxAck.h new file mode 100644 index 0000000000..88c321c445 --- /dev/null +++ b/cpp/lib/broker/TxAck.h @@ -0,0 +1,58 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxAck_ +#define _TxAck_ + +#include <algorithm> +#include <functional> +#include <list> +#include <AccumulatedAck.h> +#include <DeliveryRecord.h> +#include <TxOp.h> + +namespace qpid { + namespace broker { + /** + * Defines the transactional behaviour for acks received by a + * transactional channel. + */ + class TxAck : public TxOp{ + AccumulatedAck& acked; + std::list<DeliveryRecord>& unacked; + const std::string* const xid; + + public: + /** + * @param acked a representation of the accumulation of + * acks received + * @param unacked the record of delivered messages + */ + TxAck(AccumulatedAck& acked, std::list<DeliveryRecord>& unacked, const std::string* const xid = 0); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~TxAck(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/TxBuffer.cpp b/cpp/lib/broker/TxBuffer.cpp new file mode 100644 index 0000000000..acd3283bb7 --- /dev/null +++ b/cpp/lib/broker/TxBuffer.cpp @@ -0,0 +1,55 @@ +/* + * + * 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 <TxBuffer.h> + +using std::mem_fun; +using namespace qpid::broker; + +bool TxBuffer::prepare(TransactionalStore* const store) +{ + std::auto_ptr<TransactionContext> ctxt; + if(store) ctxt = store->begin(); + for(op_iterator i = ops.begin(); i < ops.end(); i++){ + if(!(*i)->prepare(ctxt.get())){ + if(store) store->abort(ctxt.get()); + return false; + } + } + if(store) store->commit(ctxt.get()); + return true; +} + +void TxBuffer::commit() +{ + for_each(ops.begin(), ops.end(), mem_fun(&TxOp::commit)); + ops.clear(); +} + +void TxBuffer::rollback() +{ + for_each(ops.begin(), ops.end(), mem_fun(&TxOp::rollback)); + ops.clear(); +} + +void TxBuffer::enlist(TxOp* const op) +{ + ops.push_back(op); +} diff --git a/cpp/lib/broker/TxBuffer.h b/cpp/lib/broker/TxBuffer.h new file mode 100644 index 0000000000..2d9a2a3679 --- /dev/null +++ b/cpp/lib/broker/TxBuffer.h @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxBuffer_ +#define _TxBuffer_ + +#include <algorithm> +#include <functional> +#include <vector> +#include <TransactionalStore.h> +#include <TxOp.h> + +/** + * Represents a single transaction. As such, an instance of this class + * will hold a list of operations representing the workload of the + * transaction. This work can be committed or rolled back. Committing + * is a two-stage process: first all the operations should be + * prepared, then if that succeeds they can be committed. + * + * In the 2pc case, a successful prepare may be followed by either a + * commit or a rollback. + * + * Atomicity of prepare is ensured by using a lower level + * transactional facility. This saves explicitly rolling back all the + * successfully prepared ops when one of them fails. i.e. we do not + * use 2pc internally, we instead ensure that prepare is atomic at a + * lower level. This makes individual prepare operations easier to + * code. + * + * Transactions on a messaging broker effect three types of 'action': + * (1) updates to persistent storage (2) updates to transient storage + * or cached data (3) network writes. + * + * Of these, (1) should always occur atomically during prepare to + * ensure that if the broker crashes while a transaction is being + * completed the persistent state (which is all that then remains) is + * consistent. (3) can only be done on commit, after a successful + * prepare. There is a little more flexibility with (2) but any + * changes made during prepare should be subject to the control of the + * TransactionalStore in use. + */ +namespace qpid { + namespace broker { + class TxBuffer{ + typedef std::vector<TxOp*>::iterator op_iterator; + std::vector<TxOp*> ops; + public: + /** + * Requests that all ops are prepared. This should + * primarily involve making sure that a persistent record + * of the operations is stored where necessary. + * + * All ops will be prepared under a transaction on the + * specified store. If any operation fails on prepare, + * this transaction will be rolled back. + * + * Once prepared, a transaction can be committed (or in + * the 2pc case, rolled back). + * + * @returns true if all the operations prepared + * successfully, false if not. + */ + bool prepare(TransactionalStore* const store); + /** + * Signals that the ops all prepared all completed + * successfully and can now commit, i.e. the operation can + * now be fully carried out. + * + * Should only be called after a call to prepare() returns + * true. + */ + void commit(); + /** + * Rolls back all the operations. + * + * Should only be called either after a call to prepare() + * returns true (2pc) or instead of a prepare call + * ('server-local') + */ + void rollback(); + /** + * Adds an operation to the transaction. + */ + void enlist(TxOp* const op); + }; + } +} + + +#endif diff --git a/cpp/lib/broker/TxOp.h b/cpp/lib/broker/TxOp.h new file mode 100644 index 0000000000..abba84a8e8 --- /dev/null +++ b/cpp/lib/broker/TxOp.h @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxOp_ +#define _TxOp_ + +#include <TransactionalStore.h> + +namespace qpid { + namespace broker { + class TxOp{ + public: + virtual bool prepare(TransactionContext*) throw() = 0; + virtual void commit() throw() = 0; + virtual void rollback() throw() = 0; + virtual ~TxOp(){} + }; + } +} + + +#endif diff --git a/cpp/lib/broker/TxPublish.cpp b/cpp/lib/broker/TxPublish.cpp new file mode 100644 index 0000000000..49dd8abd89 --- /dev/null +++ b/cpp/lib/broker/TxPublish.cpp @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <TxPublish.h> + +using namespace qpid::broker; + +TxPublish::TxPublish(Message::shared_ptr _msg, const std::string* const _xid) : msg(_msg), xid(_xid) {} + +bool TxPublish::prepare(TransactionContext* ctxt) throw(){ + try{ + for_each(queues.begin(), queues.end(), Prepare(ctxt, msg, xid)); + return true; + }catch(...){ + std::cout << "TxPublish::prepare() - Failed to prepare" << std::endl; + return false; + } +} + +void TxPublish::commit() throw(){ + for_each(queues.begin(), queues.end(), Commit(msg)); +} + +void TxPublish::rollback() throw(){ +} + +void TxPublish::deliverTo(Queue::shared_ptr& queue){ + queues.push_back(queue); +} + +TxPublish::Prepare::Prepare(TransactionContext* _ctxt, Message::shared_ptr& _msg, const string* const _xid) + : ctxt(_ctxt), msg(_msg), xid(_xid){} + +void TxPublish::Prepare::operator()(Queue::shared_ptr& queue){ + queue->enqueue(ctxt, msg, xid); +} + +TxPublish::Commit::Commit(Message::shared_ptr& _msg) : msg(_msg){} + +void TxPublish::Commit::operator()(Queue::shared_ptr& queue){ + queue->process(msg); +} + diff --git a/cpp/lib/broker/TxPublish.h b/cpp/lib/broker/TxPublish.h new file mode 100644 index 0000000000..75f201257e --- /dev/null +++ b/cpp/lib/broker/TxPublish.h @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TxPublish_ +#define _TxPublish_ + +#include <algorithm> +#include <functional> +#include <list> +#include <Deliverable.h> +#include <BrokerMessage.h> +#include <MessageStore.h> +#include <BrokerQueue.h> +#include <TxOp.h> + +namespace qpid { + namespace broker { + /** + * Defines the behaviour for publish operations on a + * transactional channel. Messages are routed through + * exchanges when received but are not at that stage delivered + * to the matching queues, rather the queues are held in an + * instance of this class. On prepare() the message is marked + * enqueued to the relevant queues in the MessagesStore. On + * commit() the messages will be passed to the queue for + * dispatch or to be added to the in-memory queue. + */ + class TxPublish : public TxOp, public Deliverable{ + class Prepare{ + TransactionContext* ctxt; + Message::shared_ptr& msg; + const std::string* const xid; + public: + Prepare(TransactionContext* ctxt, Message::shared_ptr& msg, const std::string* const xid); + void operator()(Queue::shared_ptr& queue); + }; + + class Commit{ + Message::shared_ptr& msg; + public: + Commit(Message::shared_ptr& msg); + void operator()(Queue::shared_ptr& queue); + }; + + Message::shared_ptr msg; + const std::string* const xid; + std::list<Queue::shared_ptr> queues; + + public: + TxPublish(Message::shared_ptr msg, const std::string* const xid = 0); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + + virtual void deliverTo(Queue::shared_ptr& queue); + + virtual ~TxPublish(){} + }; + } +} + + +#endif diff --git a/cpp/lib/client/Basic.cpp b/cpp/lib/client/Basic.cpp new file mode 100644 index 0000000000..4a1cf249a8 --- /dev/null +++ b/cpp/lib/client/Basic.cpp @@ -0,0 +1,255 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <iostream> +#include "Basic.h" +#include "AMQMethodBody.h" +#include "ClientChannel.h" +#include "ReturnedMessageHandler.h" +#include "MessageListener.h" +#include "framing/FieldTable.h" +#include "Connection.h" + +using namespace std; + +namespace qpid { +namespace client { + +using namespace sys; +using namespace framing; + +Basic::Basic(Channel& ch) : channel(ch), returnsHandler(0) {} + +void Basic::consume( + Queue& queue, std::string& tag, MessageListener* listener, + AckMode ackMode, bool noLocal, bool synch, const FieldTable* fields) +{ + channel.sendAndReceiveSync<BasicConsumeOkBody>( + synch, + new BasicConsumeBody( + channel.version, 0, queue.getName(), tag, noLocal, + ackMode == NO_ACK, false, !synch, + fields ? *fields : FieldTable())); + if (synch) { + BasicConsumeOkBody::shared_ptr response = + boost::shared_polymorphic_downcast<BasicConsumeOkBody>( + channel.responses.getResponse()); + tag = response->getConsumerTag(); + } + // FIXME aconway 2007-02-20: Race condition! + // We could receive the first message for the consumer + // before we create the consumer below. + // Move consumer creation to handler for BasicConsumeOkBody + { + Mutex::ScopedLock l(lock); + ConsumerMap::iterator i = consumers.find(tag); + if (i != consumers.end()) + THROW_QPID_ERROR(CLIENT_ERROR, + "Consumer already exists with tag="+tag); + Consumer& c = consumers[tag]; + c.listener = listener; + c.ackMode = ackMode; + c.lastDeliveryTag = 0; + } +} + + +void Basic::cancel(const std::string& tag, bool synch) { + Consumer c; + { + Mutex::ScopedLock l(lock); + ConsumerMap::iterator i = consumers.find(tag); + if (i == consumers.end()) + return; + c = i->second; + consumers.erase(i); + } + if(c.ackMode == LAZY_ACK && c.lastDeliveryTag > 0) + channel.send(new BasicAckBody(channel.version, c.lastDeliveryTag, true)); + channel.sendAndReceiveSync<BasicCancelOkBody>( + synch, new BasicCancelBody(channel.version, tag, !synch)); +} + +void Basic::cancelAll(){ + ConsumerMap consumersCopy; + { + Mutex::ScopedLock l(lock); + consumersCopy = consumers; + consumers.clear(); + } + for (ConsumerMap::iterator i=consumersCopy.begin(); + i != consumersCopy.end(); ++i) + { + Consumer& c = i->second; + if ((c.ackMode == LAZY_ACK || c.ackMode == AUTO_ACK) + && c.lastDeliveryTag > 0) + { + channel.send(new BasicAckBody(channel.version, c.lastDeliveryTag, true)); + } + } +} + + + +bool Basic::get(Message& msg, const Queue& queue, AckMode ackMode) { + // Expect a message starting with a BasicGetOk + incoming.startGet(); + channel.send(new BasicGetBody(channel.version, 0, queue.getName(), ackMode)); + return incoming.waitGet(msg); +} + + +void Basic::publish( + const Message& msg, const Exchange& exchange, + const std::string& routingKey, bool mandatory, bool immediate) +{ + const string e = exchange.getName(); + string key = routingKey; + channel.send(new BasicPublishBody(channel.version, 0, e, key, mandatory, immediate)); + //break msg up into header frame and content frame(s) and send these + channel.send(msg.getHeader()); + string data = msg.getData(); + uint64_t data_length = data.length(); + if(data_length > 0){ + //frame itself uses 8 bytes + uint32_t frag_size = channel.connection->getMaxFrameSize() - 8; + if(data_length < frag_size){ + channel.send(new AMQContentBody(data)); + }else{ + uint32_t offset = 0; + uint32_t remaining = data_length - offset; + while (remaining > 0) { + uint32_t length = remaining > frag_size ? frag_size : remaining; + string frag(data.substr(offset, length)); + channel.send(new AMQContentBody(frag)); + + offset += length; + remaining = data_length - offset; + } + } + } +} + +void Basic::handle(boost::shared_ptr<AMQMethodBody> method) { + assert(method->amqpClassId() ==BasicGetBody::CLASS_ID); + switch(method->amqpMethodId()) { + case BasicDeliverBody::METHOD_ID: + case BasicReturnBody::METHOD_ID: + case BasicGetOkBody::METHOD_ID: + case BasicGetEmptyBody::METHOD_ID: + incoming.add(method); + return; + } + throw Channel::UnknownMethod(); +} + +void Basic::deliver(Consumer& consumer, Message& msg){ + //record delivery tag: + consumer.lastDeliveryTag = msg.getDeliveryTag(); + + //allow registered listener to handle the message + consumer.listener->received(msg); + + if(channel.isOpen()){ + bool multiple(false); + switch(consumer.ackMode){ + case LAZY_ACK: + multiple = true; + if(++(consumer.count) < channel.getPrefetch()) + break; + //else drop-through + case AUTO_ACK: + consumer.lastDeliveryTag = 0; + channel.send( + new BasicAckBody( + channel.version, msg.getDeliveryTag(), multiple)); + case NO_ACK: // Nothing to do + case CLIENT_ACK: // User code must ack. + break; + // TODO aconway 2007-02-22: Provide a way for user + // to ack! + } + } + + //as it stands, transactionality is entirely orthogonal to ack + //mode, though the acks will not be processed by the broker under + //a transaction until it commits. +} + + +void Basic::run() { + while(channel.isOpen()) { + try { + Message msg = incoming.waitDispatch(); + if(msg.getMethod()->isA<BasicReturnBody>()) { + ReturnedMessageHandler* handler=0; + { + Mutex::ScopedLock l(lock); + handler=returnsHandler; + } + if(handler == 0) { + // TODO aconway 2007-02-20: proper logging. + cout << "Message returned: " << msg.getData() << endl; + } + else + handler->returned(msg); + } + else { + BasicDeliverBody::shared_ptr deliverBody = + boost::shared_polymorphic_downcast<BasicDeliverBody>( + msg.getMethod()); + std::string tag = deliverBody->getConsumerTag(); + Consumer consumer; + { + Mutex::ScopedLock l(lock); + ConsumerMap::iterator i = consumers.find(tag); + if(i == consumers.end()) + THROW_QPID_ERROR(PROTOCOL_ERROR+504, + "Unknown consumer tag=" + tag); + consumer = i->second; + } + deliver(consumer, msg); + } + } + catch (const ShutdownException&) { + /* Orderly shutdown */ + } + catch (const Exception& e) { + // FIXME aconway 2007-02-20: Report exception to user. + cout << "client::Basic::run() terminated by: " << e.toString() + << "(" << typeid(e).name() << ")" << endl; + } + } +} + +void Basic::setReturnedMessageHandler(ReturnedMessageHandler* handler){ + Mutex::ScopedLock l(lock); + returnsHandler = handler; +} + +void Basic::setQos(){ + channel.sendAndReceive<BasicQosOkBody>( + new BasicQosBody(channel.version, 0, channel.getPrefetch(), false)); + if(channel.isTransactional()) + channel.sendAndReceive<TxSelectOkBody>(new TxSelectBody(channel.version)); +} + + +// TODO aconway 2007-02-22: NOTES: +// Move incoming to BasicChannel - check for uses. + +}} // namespace qpid::client diff --git a/cpp/lib/client/Basic.h b/cpp/lib/client/Basic.h new file mode 100644 index 0000000000..f6ae633ab8 --- /dev/null +++ b/cpp/lib/client/Basic.h @@ -0,0 +1,195 @@ +#ifndef _client_Basic_h +#define _client_Basic_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "IncomingMessage.h" +#include "sys/Runnable.h" + +namespace qpid { + +namespace framing { +class AMQMethodBody; +class FieldTable; +} + +namespace client { + +class Channel; +class Message; +class Queue; +class Exchange; +class MessageListener; +class ReturnedMessageHandler; + +/** + * The available acknowledgements modes. + * + * \ingroup clientapi + */ +enum AckMode { + /** No acknowledgement will be sent, broker can + discard messages as soon as they are delivered + to a consumer using this mode. **/ + NO_ACK = 0, + /** Each message will be automatically + acknowledged as soon as it is delivered to the + application **/ + AUTO_ACK = 1, + /** Acknowledgements will be sent automatically, + but not for each message. **/ + LAZY_ACK = 2, + /** The application is responsible for explicitly + acknowledging messages. **/ + CLIENT_ACK = 3 +}; + + +/** + * Represents the AMQP Basic class for sending and receiving messages. + */ +class Basic : public sys::Runnable +{ + public: + Basic(Channel& parent); + + /** + * Creates a 'consumer' for a queue. Messages in (or arriving + * at) that queue will be delivered to consumers + * asynchronously. + * + * @param queue a Queue instance representing the queue to + * consume from + * + * @param tag an identifier to associate with the consumer + * that can be used to cancel its subscription (if empty, this + * will be assigned by the broker) + * + * @param listener a pointer to an instance of an + * implementation of the MessageListener interface. Messages + * received from this queue for this consumer will result in + * invocation of the received() method on the listener, with + * the message itself passed in. + * + * @param ackMode the mode of acknowledgement that the broker + * should assume for this consumer. @see AckMode + * + * @param noLocal if true, this consumer will not be sent any + * message published by this connection + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void consume( + Queue& queue, std::string& tag, MessageListener* listener, + AckMode ackMode = NO_ACK, bool noLocal = false, bool synch = true, + const framing::FieldTable* fields = 0); + + /** + * Cancels a subscription previously set up through a call to consume(). + * + * @param tag the identifier used (or assigned) in the consume + * request that set up the subscription to be cancelled. + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void cancel(const std::string& tag, bool synch = true); + /** + * Synchronous pull of a message from a queue. + * + * @param msg a message object that will contain the message + * headers and content if the call completes. + * + * @param queue the queue to consume from + * + * @param ackMode the acknowledgement mode to use (@see + * AckMode) + * + * @return true if a message was succcessfully dequeued from + * the queue, false if the queue was empty. + */ + bool get(Message& msg, const Queue& queue, AckMode ackMode = NO_ACK); + + /** + * Publishes (i.e. sends a message to the broker). + * + * @param msg the message to publish + * + * @param exchange the exchange to publish the message to + * + * @param routingKey the routing key to publish with + * + * @param mandatory if true and the exchange to which this + * publish is directed has no matching bindings, the message + * will be returned (see setReturnedMessageHandler()). + * + * @param immediate if true and there is no consumer to + * receive this message on publication, the message will be + * returned (see setReturnedMessageHandler()). + */ + void publish(const Message& msg, const Exchange& exchange, + const std::string& routingKey, + bool mandatory = false, bool immediate = false); + + /** + * Set a handler for this channel that will process any + * returned messages + * + * @see publish() + */ + void setReturnedMessageHandler(ReturnedMessageHandler* handler); + + /** + * Deliver messages from the broker to the appropriate MessageListener. + */ + void run(); + + + private: + + struct Consumer{ + MessageListener* listener; + AckMode ackMode; + int count; + uint64_t lastDeliveryTag; + }; + + typedef std::map<std::string, Consumer> ConsumerMap; + + void handle(boost::shared_ptr<framing::AMQMethodBody>); + void setQos(); + void cancelAll(); + void deliver(Consumer& consumer, Message& msg); + + sys::Mutex lock; + Channel& channel; + IncomingMessage incoming; + ConsumerMap consumers; + ReturnedMessageHandler* returnsHandler; + + // FIXME aconway 2007-02-22: Remove friendship. + friend class Channel; +}; + +}} // namespace qpid::client + + + +#endif /*!_client_Basic_h*/ diff --git a/cpp/lib/client/ClientAdapter.cpp b/cpp/lib/client/ClientAdapter.cpp new file mode 100644 index 0000000000..c77f049c96 --- /dev/null +++ b/cpp/lib/client/ClientAdapter.cpp @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "AMQP_ClientOperations.h" +#include "ClientAdapter.h" +#include "Connection.h" +#include "Exception.h" +#include "AMQMethodBody.h" + +namespace qpid { +namespace client { + +using namespace qpid; +using namespace qpid::framing; + +typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; + +void ClientAdapter::handleMethodInContext( + boost::shared_ptr<qpid::framing::AMQMethodBody> method, + const MethodContext& context +) +{ + try{ + method->invoke(*clientOps, context); + }catch(ChannelException& e){ + connection.client->getChannel().close( + context, e.code, e.toString(), + method->amqpClassId(), method->amqpMethodId()); + connection.closeChannel(getId()); + }catch(ConnectionException& e){ + connection.client->getConnection().close( + context, e.code, e.toString(), + method->amqpClassId(), method->amqpMethodId()); + }catch(std::exception& e){ + connection.client->getConnection().close( + context, 541/*internal error*/, e.what(), + method->amqpClassId(), method->amqpMethodId()); + } +} + +void ClientAdapter::handleHeader(AMQHeaderBody::shared_ptr body) { + channel->handleHeader(body); +} + +void ClientAdapter::handleContent(AMQContentBody::shared_ptr body) { + channel->handleContent(body); +} + +void ClientAdapter::handleHeartbeat(AMQHeartbeatBody::shared_ptr) { + // TODO aconway 2007-01-17: Implement heartbeats. +} + + + +}} // namespace qpid::client + diff --git a/cpp/lib/client/ClientAdapter.h b/cpp/lib/client/ClientAdapter.h new file mode 100644 index 0000000000..d5e16fc6ad --- /dev/null +++ b/cpp/lib/client/ClientAdapter.h @@ -0,0 +1,66 @@ +#ifndef _client_ClientAdapter_h +#define _client_ClientAdapter_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "ChannelAdapter.h" +#include "ClientChannel.h" + +namespace qpid { +namespace client { + +class AMQMethodBody; +class Connection; + +/** + * Per-channel protocol adapter. + * + * Translates protocol bodies into calls on the core Channel, + * Connection and Client objects. + * + * Owns a channel, has references to Connection and Client. + */ +class ClientAdapter : public framing::ChannelAdapter +{ + public: + ClientAdapter(std::auto_ptr<Channel> ch, Connection&, Client&); + Channel& getChannel() { return *channel; } + + void handleHeader(boost::shared_ptr<qpid::framing::AMQHeaderBody>); + void handleContent(boost::shared_ptr<qpid::framing::AMQContentBody>); + void handleHeartbeat(boost::shared_ptr<qpid::framing::AMQHeartbeatBody>); + + private: + void handleMethodInContext( + boost::shared_ptr<qpid::framing::AMQMethodBody> method, + const framing::MethodContext& context); + + class ClientOps; + + std::auto_ptr<Channel> channel; + Connection& connection; + Client& client; + boost::shared_ptr<ClientOps> clientOps; +}; + +}} // namespace qpid::client + + + +#endif /*!_client_ClientAdapter_h*/ diff --git a/cpp/lib/client/ClientChannel.cpp b/cpp/lib/client/ClientChannel.cpp new file mode 100644 index 0000000000..84aa73e6bc --- /dev/null +++ b/cpp/lib/client/ClientChannel.cpp @@ -0,0 +1,302 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <ClientChannel.h> +#include <sys/Monitor.h> +#include <ClientMessage.h> +#include <QpidError.h> +#include <MethodBodyInstances.h> +#include "Connection.h" + +// FIXME aconway 2007-01-26: Evaluate all throws, ensure consistent +// handling of errors that should close the connection or the channel. +// Make sure the user thread receives a connection in each case. +// +using namespace std; +using namespace boost; +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + +Channel::Channel(bool _transactional, uint16_t _prefetch) : + basic(*this), + connection(0), + prefetch(_prefetch), + transactional(_transactional) +{ } + +Channel::~Channel(){ + close(); +} + +void Channel::open(ChannelId id, Connection& con) +{ + if (isOpen()) + THROW_QPID_ERROR(INTERNAL_ERROR, "Attempt to re-open channel "+id); + connection = &con; + init(id, con, con.getVersion()); // ChannelAdapter initialization. + string oob; + if (id != 0) + sendAndReceive<ChannelOpenOkBody>(new ChannelOpenBody(version, oob)); +} + +void Channel::protocolInit( + const std::string& uid, const std::string& pwd, const std::string& vhost) { + assert(connection); + responses.expect(); + connection->connector->init(); // Send ProtocolInit block. + responses.receive<ConnectionStartBody>(); + + FieldTable props; + string mechanism("PLAIN"); + string response = ((char)0) + uid + ((char)0) + pwd; + string locale("en_US"); + ConnectionTuneBody::shared_ptr proposal = + sendAndReceive<ConnectionTuneBody>( + new ConnectionStartOkBody( + version, responses.getRequestId(), props, mechanism, + response, locale)); + + /** + * Assume for now that further challenges will not be required + //receive connection.secure + responses.receive(connection_secure)); + //send connection.secure-ok + connection->send(new AMQFrame(0, new ConnectionSecureOkBody(response))); + **/ + + send(new ConnectionTuneOkBody( + version, responses.getRequestId(), proposal->getChannelMax(), connection->getMaxFrameSize(), + proposal->getHeartbeat())); + + uint16_t heartbeat = proposal->getHeartbeat(); + connection->connector->setReadTimeout(heartbeat * 2); + connection->connector->setWriteTimeout(heartbeat); + + // Send connection open. + std::string capabilities; + responses.expect(); + send(new ConnectionOpenBody(version, vhost, capabilities, true)); + //receive connection.open-ok (or redirect, but ignore that for now + //esp. as using force=true). + responses.waitForResponse(); + if(responses.validate<ConnectionOpenOkBody>()) { + //ok + }else if(responses.validate<ConnectionRedirectBody>()){ + //ignore for now + ConnectionRedirectBody::shared_ptr redirect( + shared_polymorphic_downcast<ConnectionRedirectBody>( + responses.getResponse())); + cout << "Received redirection to " << redirect->getHost() + << endl; + } else { + THROW_QPID_ERROR(PROTOCOL_ERROR, "Bad response"); + } +} + +bool Channel::isOpen() const { return connection; } + +void Channel::setQos() { + basic.setQos(); + // FIXME aconway 2007-02-22: message +} + +void Channel::setPrefetch(uint16_t _prefetch){ + prefetch = _prefetch; + setQos(); +} + +void Channel::declareExchange(Exchange& exchange, bool synch){ + string name = exchange.getName(); + string type = exchange.getType(); + FieldTable args; + sendAndReceiveSync<ExchangeDeclareOkBody>( + synch, + new ExchangeDeclareBody( + version, 0, name, type, false, false, false, false, !synch, args)); +} + +void Channel::deleteExchange(Exchange& exchange, bool synch){ + string name = exchange.getName(); + sendAndReceiveSync<ExchangeDeleteOkBody>( + synch, + new ExchangeDeleteBody(version, 0, name, false, !synch)); +} + +void Channel::declareQueue(Queue& queue, bool synch){ + string name = queue.getName(); + FieldTable args; + sendAndReceiveSync<QueueDeclareOkBody>( + synch, + new QueueDeclareBody( + version, 0, name, false/*passive*/, queue.isDurable(), + queue.isExclusive(), queue.isAutoDelete(), !synch, args)); + if(synch){ + if(queue.getName().length() == 0){ + QueueDeclareOkBody::shared_ptr response = + shared_polymorphic_downcast<QueueDeclareOkBody>( + responses.getResponse()); + queue.setName(response->getQueue()); + } + } +} + +void Channel::deleteQueue(Queue& queue, bool ifunused, bool ifempty, bool synch){ + //ticket, queue, ifunused, ifempty, nowait + string name = queue.getName(); + sendAndReceiveSync<QueueDeleteOkBody>( + synch, + new QueueDeleteBody(version, 0, name, ifunused, ifempty, !synch)); +} + +void Channel::bind(const Exchange& exchange, const Queue& queue, const std::string& key, const FieldTable& args, bool synch){ + string e = exchange.getName(); + string q = queue.getName(); + sendAndReceiveSync<QueueBindOkBody>( + synch, + new QueueBindBody(version, 0, q, e, key,!synch, args)); +} + +void Channel::commit(){ + sendAndReceive<TxCommitOkBody>(new TxCommitBody(version)); +} + +void Channel::rollback(){ + sendAndReceive<TxRollbackOkBody>(new TxRollbackBody(version)); +} + +void Channel::handleMethodInContext( + AMQMethodBody::shared_ptr method, const MethodContext&) +{ + if(responses.isWaiting()) { + responses.signalResponse(method); + return; + } + try { + switch (method->amqpClassId()) { + case BasicDeliverBody::CLASS_ID: basic.handle(method); break; + case ChannelCloseBody::CLASS_ID: handleChannel(method); break; + case ConnectionCloseBody::CLASS_ID: handleConnection(method); break; + default: throw UnknownMethod(); + } + } + catch (const UnknownMethod&) { + connection->close( + 504, "Unknown method", + method->amqpClassId(), method->amqpMethodId()); + } +} + +void Channel::handleChannel(AMQMethodBody::shared_ptr method) { + switch (method->amqpMethodId()) { + case ChannelCloseBody::METHOD_ID: + peerClose(shared_polymorphic_downcast<ChannelCloseBody>(method)); + return; + case ChannelFlowBody::METHOD_ID: + // FIXME aconway 2007-02-22: Not yet implemented. + return; + } + throw UnknownMethod(); +} + +void Channel::handleConnection(AMQMethodBody::shared_ptr method) { + if (method->amqpMethodId() == ConnectionCloseBody::METHOD_ID) { + connection->close(); + return; + } + throw UnknownMethod(); +} + +void Channel::handleHeader(AMQHeaderBody::shared_ptr body){ + basic.incoming.add(body); +} + +void Channel::handleContent(AMQContentBody::shared_ptr body){ + basic.incoming.add(body); +} + +void Channel::handleHeartbeat(AMQHeartbeatBody::shared_ptr /*body*/){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Channel received heartbeat"); +} + +void Channel::start(){ + basicDispatcher = Thread(basic); +} + +// Close called by local application. +void Channel::close( + uint16_t code, const std::string& text, + ClassId classId, MethodId methodId) +{ + if (isOpen()) { + try { + if (getId() != 0) { + sendAndReceive<ChannelCloseOkBody>( + new ChannelCloseBody( + version, code, text, classId, methodId)); + } + static_cast<ConnectionForChannel*>(connection)->erase(getId()); + closeInternal(); + } catch (...) { + static_cast<ConnectionForChannel*>(connection)->erase(getId()); + closeInternal(); + throw; + } + } +} + +// Channel closed by peer. +void Channel::peerClose(ChannelCloseBody::shared_ptr) { + assert(isOpen()); + closeInternal(); + // FIXME aconway 2007-01-26: How to throw the proper exception + // to the application thread? +} + +void Channel::closeInternal() { + if (isOpen()); + { + basic.cancelAll(); + basic.incoming.shutdown(); + connection = 0; + // A 0 response means we are closed. + responses.signalResponse(AMQMethodBody::shared_ptr()); + } + basicDispatcher.join(); +} + +void Channel::sendAndReceive(AMQMethodBody* toSend, ClassId c, MethodId m) +{ + responses.expect(); + send(toSend); + responses.receive(c, m); +} + +void Channel::sendAndReceiveSync( + bool sync, AMQMethodBody* body, ClassId c, MethodId m) +{ + if(sync) + sendAndReceive(body, c, m); + else + send(body); +} + + diff --git a/cpp/lib/client/ClientChannel.h b/cpp/lib/client/ClientChannel.h new file mode 100644 index 0000000000..3ecab05d0b --- /dev/null +++ b/cpp/lib/client/ClientChannel.h @@ -0,0 +1,250 @@ +#ifndef _client_ClientChannel_h +#define _client_ClientChannel_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 "sys/types.h" +#include <framing/amqp_framing.h> +#include <ClientExchange.h> +#include <ClientMessage.h> +#include <ClientQueue.h> +#include <ResponseHandler.h> +#include "ChannelAdapter.h" +#include "Thread.h" +#include "Basic.h" + +namespace qpid { + +namespace framing { +class ChannelCloseBody; +class AMQMethodBody; +} + +namespace client { + +class Connection; + + +/** + * Represents an AMQP channel, i.e. loosely a session of work. It + * is through a channel that most of the AMQP 'methods' are + * exposed. + * + * \ingroup clientapi + */ +class Channel : public framing::ChannelAdapter +{ + private: + // TODO aconway 2007-02-22: Remove friendship. + friend class Basic; + // FIXME aconway 2007-02-22: friend class Message; + + struct UnknownMethod {}; + + sys::Mutex lock; + Basic basic; + Connection* connection; + sys::Thread basicDispatcher; + ResponseHandler responses; + + uint16_t prefetch; + const bool transactional; + framing::ProtocolVersion version; + + void handleHeader(framing::AMQHeaderBody::shared_ptr body); + void handleContent(framing::AMQContentBody::shared_ptr body); + void handleHeartbeat(framing::AMQHeartbeatBody::shared_ptr body); + void handleMethodInContext( + framing::AMQMethodBody::shared_ptr, const framing::MethodContext&); + void handleChannel(framing::AMQMethodBody::shared_ptr method); + void handleConnection(framing::AMQMethodBody::shared_ptr method); + + void setQos(); + + void protocolInit( + const std::string& uid, const std::string& pwd, + const std::string& vhost); + + void sendAndReceive( + framing::AMQMethodBody*, framing::ClassId, framing::MethodId); + + void sendAndReceiveSync( + bool sync, + framing::AMQMethodBody*, framing::ClassId, framing::MethodId); + + template <class BodyType> + boost::shared_ptr<BodyType> sendAndReceive(framing::AMQMethodBody* body) { + sendAndReceive(body, BodyType::CLASS_ID, BodyType::METHOD_ID); + return boost::shared_polymorphic_downcast<BodyType>( + responses.getResponse()); + } + + template <class BodyType> void sendAndReceiveSync( + bool sync, framing::AMQMethodBody* body) { + sendAndReceiveSync( + sync, body, BodyType::CLASS_ID, BodyType::METHOD_ID); + } + + void open(framing::ChannelId, Connection&); + void closeInternal(); + void peerClose(boost::shared_ptr<framing::ChannelCloseBody>); + + friend class Connection; + + public: + + /** + * Creates a channel object. + * + * @param transactional if true, the publishing and acknowledgement + * of messages will be transactional and can be committed or + * aborted in atomic units (@see commit(), @see rollback()) + * + * @param prefetch specifies the number of unacknowledged + * messages the channel is willing to have sent to it + * asynchronously + */ + Channel(bool transactional = false, uint16_t prefetch = 500); + ~Channel(); + + /** + * Declares an exchange. + * + * In AMQP Exchanges are the destinations to which messages + * are published. They have Queues bound to them and route + * messages they receive to those queues. The routing rules + * depend on the type of the exchange. + * + * @param exchange an Exchange object representing the + * exchange to declare + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void declareExchange(Exchange& exchange, bool synch = true); + /** + * Deletes an exchange + * + * @param exchange an Exchange object representing the exchange to delete + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void deleteExchange(Exchange& exchange, bool synch = true); + /** + * Declares a Queue + * + * @param queue a Queue object representing the queue to declare + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void declareQueue(Queue& queue, bool synch = true); + /** + * Deletes a Queue + * + * @param queue a Queue object representing the queue to delete + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void deleteQueue(Queue& queue, bool ifunused = false, bool ifempty = false, bool synch = true); + /** + * Binds a queue to an exchange. The exact semantics of this + * (in particular how 'routing keys' and 'binding arguments' + * are used) depends on the type of the exchange. + * + * @param exchange an Exchange object representing the + * exchange to bind to + * + * @param queue a Queue object representing the queue to be + * bound + * + * @param key the 'routing key' for the binding + * + * @param args the 'binding arguments' for the binding + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void bind(const Exchange& exchange, const Queue& queue, + const std::string& key, const framing::FieldTable& args, + bool synch = true); + + /** + * Get a Basic object which provides functions to send and + * receive messages using the AMQP 0-8 Basic class methods. + *@see Basic + */ + Basic& getBasic() { return basic; } + + /** + * For a transactional channel this will commit all + * publications and acknowledgements since the last commit (or + * the channel was opened if there has been no previous + * commit). This will cause published messages to become + * available to consumers and acknowledged messages to be + * consumed and removed from the queues they were dispatched + * from. + * + * Transactionailty of a channel is specified when the channel + * object is created (@see Channel()). + */ + void commit(); + + /** + * For a transactional channel, this will rollback any + * publications or acknowledgements. It will be as if the + * ppblished messages were never sent and the acknowledged + * messages were never consumed. + */ + void rollback(); + + /** + * Change the prefetch in use. + */ + void setPrefetch(uint16_t prefetch); + + uint16_t getPrefetch() { return prefetch; } + + /** + * Start message dispatching on a new thread + */ + void start(); + + /** + * Close the channel with optional error information. + * Closing a channel that is not open has no effect. + */ + void close( + framing::ReplyCode = 200, const std::string& ="OK", + framing::ClassId = 0, framing::MethodId = 0); + + /** True if the channel is transactional */ + bool isTransactional() { return transactional; } + + /** True if the channel is open */ + bool isOpen() const; +}; + +}} + +#endif /*!_client_ClientChannel_h*/ diff --git a/cpp/lib/client/ClientExchange.cpp b/cpp/lib/client/ClientExchange.cpp new file mode 100644 index 0000000000..5e5f3f14c6 --- /dev/null +++ b/cpp/lib/client/ClientExchange.cpp @@ -0,0 +1,34 @@ +/* + * + * 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 <ClientExchange.h> + +qpid::client::Exchange::Exchange(std::string _name, std::string _type) : name(_name), type(_type){} +const std::string& qpid::client::Exchange::getName() const { return name; } +const std::string& qpid::client::Exchange::getType() const { return type; } + +const std::string qpid::client::Exchange::DIRECT_EXCHANGE = "direct"; +const std::string qpid::client::Exchange::TOPIC_EXCHANGE = "topic"; +const std::string qpid::client::Exchange::HEADERS_EXCHANGE = "headers"; + +const qpid::client::Exchange qpid::client::Exchange::DEFAULT_EXCHANGE("", DIRECT_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::STANDARD_DIRECT_EXCHANGE("amq.direct", DIRECT_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::STANDARD_TOPIC_EXCHANGE("amq.topic", TOPIC_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::STANDARD_HEADERS_EXCHANGE("amq.headers", HEADERS_EXCHANGE); diff --git a/cpp/lib/client/ClientExchange.h b/cpp/lib/client/ClientExchange.h new file mode 100644 index 0000000000..a8ac21fa9b --- /dev/null +++ b/cpp/lib/client/ClientExchange.h @@ -0,0 +1,106 @@ +/* + * + * 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 <string> + +#ifndef _Exchange_ +#define _Exchange_ + +namespace qpid { +namespace client { + + /** + * A 'handle' used to represent an AMQP exchange in the Channel + * methods. Exchanges are the destinations to which messages are + * published. + * + * There are different types of exchange (the standard types are + * available as static constants, see DIRECT_EXCHANGE, + * TOPIC_EXCHANGE and HEADERS_EXCHANGE). A Queue can be bound to + * an exchange using Channel::bind() and messages published to + * that exchange are then routed to the queue based on the details + * of the binding and the type of exchange. + * + * There are some standard exchange instances that are predeclared + * on all AMQP brokers. These are defined as static members + * STANDARD_DIRECT_EXCHANGE, STANDARD_TOPIC_EXCHANGE and + * STANDARD_HEADERS_EXCHANGE. There is also the 'default' exchange + * (member DEFAULT_EXCHANGE) which is nameless and of type + * 'direct' and has every declared queue bound to it by queue + * name. + * + * \ingroup clientapi + */ + class Exchange{ + const std::string name; + const std::string type; + + public: + /** + * A direct exchange routes messages published with routing + * key X to any queue bound with key X (i.e. an exact match is + * used). + */ + static const std::string DIRECT_EXCHANGE; + /** + * A topic exchange treat the key with which a queue is bound + * as a pattern and routes all messages whose routing keys + * match that pattern to the bound queue. The routing key for + * a message must consist of zero or more alpha-numeric words + * delimited by dots. The pattern is of a similar form but * + * can be used to match excatly one word and # can be used to + * match zero or more words. + */ + static const std::string TOPIC_EXCHANGE; + /** + * The headers exchange routes messages based on whether their + * headers match the binding arguments specified when + * binding. (see the AMQP spec for more details). + */ + static const std::string HEADERS_EXCHANGE; + + /** + * The 'default' exchange, nameless and of type 'direct'. Has + * every declared queue bound to it by name. + */ + static const Exchange DEFAULT_EXCHANGE; + /** + * The standard direct exchange, named amq.direct. + */ + static const Exchange STANDARD_DIRECT_EXCHANGE; + /** + * The standard topic exchange, named amq.topic. + */ + static const Exchange STANDARD_TOPIC_EXCHANGE; + /** + * The standard headers exchange, named amq.header. + */ + static const Exchange STANDARD_HEADERS_EXCHANGE; + + Exchange(std::string name, std::string type = DIRECT_EXCHANGE); + const std::string& getName() const; + const std::string& getType() const; + }; + +} +} + + +#endif diff --git a/cpp/lib/client/ClientMessage.cpp b/cpp/lib/client/ClientMessage.cpp new file mode 100644 index 0000000000..f55c4abfe6 --- /dev/null +++ b/cpp/lib/client/ClientMessage.cpp @@ -0,0 +1,162 @@ +/* + * + * 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 <ClientMessage.h> +using namespace qpid::client; +using namespace qpid::framing; + +Message::Message(const std::string& d) + : header(new AMQHeaderBody(BASIC)) +{ + setData(d); +} + +void Message::setData(const std::string& d) { + data = d; + header->setContentSize(d.size()); +} + +Message::Message(AMQHeaderBody::shared_ptr& _header) : header(_header){ +} + +Message::~Message(){ +} + +BasicHeaderProperties* Message::getHeaderProperties() const { + return dynamic_cast<BasicHeaderProperties*>(header->getProperties()); +} + +const std::string& Message::getContentType() const { + return getHeaderProperties()->getContentType(); +} + +const std::string& Message::getContentEncoding() const { + return getHeaderProperties()->getContentEncoding(); +} + +FieldTable& Message::getHeaders() const { + return getHeaderProperties()->getHeaders(); +} + +uint8_t Message::getDeliveryMode() const { + return getHeaderProperties()->getDeliveryMode(); +} + +uint8_t Message::getPriority() const { + return getHeaderProperties()->getPriority(); +} + +const std::string& Message::getCorrelationId() const { + return getHeaderProperties()->getCorrelationId(); +} + +const std::string& Message::getReplyTo() const { + return getHeaderProperties()->getReplyTo(); +} + +const std::string& Message::getExpiration() const { + return getHeaderProperties()->getExpiration(); +} + +const std::string& Message::getMessageId() const { + return getHeaderProperties()->getMessageId(); +} + +uint64_t Message::getTimestamp() const { + return getHeaderProperties()->getTimestamp(); +} + +const std::string& Message::getType() const { + return getHeaderProperties()->getType(); +} + +const std::string& Message::getUserId() const { + return getHeaderProperties()->getUserId(); +} + +const std::string& Message::getAppId() const { + return getHeaderProperties()->getAppId(); +} + +const std::string& Message::getClusterId() const { + return getHeaderProperties()->getClusterId(); +} + +void Message::setContentType(const std::string& type){ + getHeaderProperties()->setContentType(type); +} + +void Message::setContentEncoding(const std::string& encoding){ + getHeaderProperties()->setContentEncoding(encoding); +} + +void Message::setHeaders(const FieldTable& headers){ + getHeaderProperties()->setHeaders(headers); +} + +void Message::setDeliveryMode(uint8_t mode){ + getHeaderProperties()->setDeliveryMode(mode); +} + +void Message::setPriority(uint8_t priority){ + getHeaderProperties()->setPriority(priority); +} + +void Message::setCorrelationId(const std::string& correlationId){ + getHeaderProperties()->setCorrelationId(correlationId); +} + +void Message::setReplyTo(const std::string& replyTo){ + getHeaderProperties()->setReplyTo(replyTo); +} + +void Message::setExpiration(const std::string& expiration){ + getHeaderProperties()->setExpiration(expiration); +} + +void Message::setMessageId(const std::string& messageId){ + getHeaderProperties()->setMessageId(messageId); +} + +void Message::setTimestamp(uint64_t timestamp){ + getHeaderProperties()->setTimestamp(timestamp); +} + +void Message::setType(const std::string& type){ + getHeaderProperties()->setType(type); +} + +void Message::setUserId(const std::string& userId){ + getHeaderProperties()->setUserId(userId); +} + +void Message::setAppId(const std::string& appId){ + getHeaderProperties()->setAppId(appId); +} + +void Message::setClusterId(const std::string& clusterId){ + getHeaderProperties()->setClusterId(clusterId); +} + + +uint64_t Message::getDeliveryTag() const { + BasicDeliverBody* deliver=dynamic_cast<BasicDeliverBody*>(method.get()); + return deliver ? deliver->getDeliveryTag() : 0; +} diff --git a/cpp/lib/client/ClientMessage.h b/cpp/lib/client/ClientMessage.h new file mode 100644 index 0000000000..cb239ed4d6 --- /dev/null +++ b/cpp/lib/client/ClientMessage.h @@ -0,0 +1,125 @@ +#ifndef _client_ClientMessage_h +#define _client_ClientMessage_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 <string> +#include <framing/amqp_framing.h> + +namespace qpid { + +namespace client { +class IncomingMessage; + +/** + * A representation of messages for sent or recived through the + * client api. + * + * \ingroup clientapi + */ +class Message { + framing::AMQMethodBody::shared_ptr method; + framing::AMQHeaderBody::shared_ptr header; + std::string data; + bool redelivered; + + // FIXME aconway 2007-02-20: const incorrect, needs const return type. + framing::BasicHeaderProperties* getHeaderProperties() const; + Message(qpid::framing::AMQHeaderBody::shared_ptr& header); + + public: + Message(const std::string& data=std::string()); + ~Message(); + + /** + * Allows the application to access the content of messages + * received. + * + * @return a string representing the data of the message + */ + std::string getData() const { return data; } + + /** + * Allows the application to set the content of messages to be + * sent. + * + * @param data a string representing the data of the message + */ + void setData(const std::string& _data); + + /** + * @return true if this message was delivered previously (to + * any consumer) but was not acknowledged. + */ + bool isRedelivered(){ return redelivered; } + void setRedelivered(bool _redelivered){ redelivered = _redelivered; } + + uint64_t getDeliveryTag() const; + + const std::string& getContentType() const; + const std::string& getContentEncoding() const; + qpid::framing::FieldTable& getHeaders() const; + uint8_t getDeliveryMode() const; + uint8_t getPriority() const; + const std::string& getCorrelationId() const; + const std::string& getReplyTo() const; + const std::string& getExpiration() const; + const std::string& getMessageId() const; + uint64_t getTimestamp() const; + const std::string& getType() const; + const std::string& getUserId() const; + const std::string& getAppId() const; + const std::string& getClusterId() const; + + void setContentType(const std::string& type); + void setContentEncoding(const std::string& encoding); + void setHeaders(const qpid::framing::FieldTable& headers); + /** + * Sets the delivery mode. 1 = non-durable, 2 = durable. + */ + void setDeliveryMode(uint8_t mode); + void setPriority(uint8_t priority); + void setCorrelationId(const std::string& correlationId); + void setReplyTo(const std::string& replyTo); + void setExpiration(const std::string& expiration); + void setMessageId(const std::string& messageId); + void setTimestamp(uint64_t timestamp); + void setType(const std::string& type); + void setUserId(const std::string& userId); + void setAppId(const std::string& appId); + void setClusterId(const std::string& clusterId); + + /** Get the method used to deliver this message */ + boost::shared_ptr<framing::AMQMethodBody> getMethod() const + { return method; } + + void setMethod(framing::AMQMethodBody::shared_ptr m) { method=m; } + boost::shared_ptr<framing::AMQHeaderBody> getHeader() const + { return header; } + + // TODO aconway 2007-02-15: remove friendships. + friend class IncomingMessage; + friend class Channel; +}; + +}} + +#endif /*!_client_ClientMessage_h*/ diff --git a/cpp/lib/client/ClientQueue.cpp b/cpp/lib/client/ClientQueue.cpp new file mode 100644 index 0000000000..773be504d8 --- /dev/null +++ b/cpp/lib/client/ClientQueue.cpp @@ -0,0 +1,58 @@ +/* + * + * 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 <ClientQueue.h> + +qpid::client::Queue::Queue() : name(""), autodelete(true), exclusive(true), durable(false){} + +qpid::client::Queue::Queue(std::string _name) : name(_name), autodelete(false), exclusive(false), durable(false){} + +qpid::client::Queue::Queue(std::string _name, bool temp) : name(_name), autodelete(temp), exclusive(temp), durable(false){} + +qpid::client::Queue::Queue(std::string _name, bool _autodelete, bool _exclusive, bool _durable) + : name(_name), autodelete(_autodelete), exclusive(_exclusive), durable(_durable){} + +const std::string& qpid::client::Queue::getName() const{ + return name; +} + +void qpid::client::Queue::setName(const std::string& _name){ + name = _name; +} + +bool qpid::client::Queue::isAutoDelete() const{ + return autodelete; +} + +bool qpid::client::Queue::isExclusive() const{ + return exclusive; +} + +bool qpid::client::Queue::isDurable() const{ + return durable; +} + +void qpid::client::Queue::setDurable(bool _durable){ + durable = _durable; +} + + + + diff --git a/cpp/lib/client/ClientQueue.h b/cpp/lib/client/ClientQueue.h new file mode 100644 index 0000000000..b37a44b004 --- /dev/null +++ b/cpp/lib/client/ClientQueue.h @@ -0,0 +1,103 @@ +#ifndef _client_ClientQueue_h +#define _client_ClientQueue_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 <string> + +namespace qpid { +namespace client { + + /** + * A 'handle' used to represent an AMQP queue in the Channel + * methods. Creating an instance of this class does not cause the + * queue to be created on the broker. Rather, an instance of this + * class should be passed to Channel::declareQueue() to ensure + * that the queue exists or is created. + * + * Queues hold messages and allow clients to consume + * (see Channel::consume()) or get (see Channel::get()) those messags. A + * queue receives messages by being bound to one or more Exchange; + * messages published to that exchange may then be routed to the + * queue based on the details of the binding and the type of the + * exchange (see Channel::bind()). + * + * Queues are identified by a name. They can be exclusive (in which + * case they can only be used in the context of the connection + * over which they were declared, and are deleted when then + * connection closes), or they can be shared. Shared queues can be + * auto deleted when they have no consumers. + * + * We use the term 'temporary queue' to refer to an exclusive + * queue. + * + * \ingroup clientapi + */ + class Queue{ + std::string name; + const bool autodelete; + const bool exclusive; + bool durable; + + public: + + /** + * Creates an unnamed, non-durable, temporary queue. A name + * will be assigned to this queue instance by a call to + * Channel::declareQueue(). + */ + Queue(); + /** + * Creates a shared, non-durable, queue with a given name, + * that will not be autodeleted. + * + * @param name the name of the queue + */ + Queue(std::string name); + /** + * Creates a non-durable queue with a given name. + * + * @param name the name of the queue + * + * @param temp if true the queue will be a temporary queue, if + * false it will be shared and not autodeleted. + */ + Queue(std::string name, bool temp); + /** + * This constructor allows the autodelete, exclusive and + * durable propeties to be explictly set. Note however that if + * exclusive is true, autodelete has no meaning as exclusive + * queues are always destroyed when the connection that + * created them is closed. + */ + Queue(std::string name, bool autodelete, bool exclusive, bool durable); + const std::string& getName() const; + void setName(const std::string&); + bool isAutoDelete() const; + bool isExclusive() const; + bool isDurable() const; + void setDurable(bool durable); + }; + +} +} + +#endif /*!_client_ClientQueue_h*/ diff --git a/cpp/lib/client/Connection.cpp b/cpp/lib/client/Connection.cpp new file mode 100644 index 0000000000..618f3cbc92 --- /dev/null +++ b/cpp/lib/client/Connection.cpp @@ -0,0 +1,156 @@ +/* + * + * 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 <algorithm> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +#include <Connection.h> +#include <ClientChannel.h> +#include <ClientMessage.h> +#include <QpidError.h> +#include <iostream> +#include <sstream> +#include <MethodBodyInstances.h> +#include <functional> + +using namespace qpid::framing; +using namespace qpid::sys; + + +namespace qpid { +namespace client { + +const std::string Connection::OK("OK"); + +Connection::Connection( + bool _debug, uint32_t _max_frame_size, + framing::ProtocolVersion _version +) : channelIdCounter(0), version(_version), max_frame_size(_max_frame_size), + defaultConnector(version, _debug, _max_frame_size), + isOpen(false), debug(_debug) +{ + setConnector(defaultConnector); +} + +Connection::~Connection(){} + +void Connection::setConnector(Connector& con) +{ + connector = &con; + connector->setInputHandler(this); + connector->setTimeoutHandler(this); + connector->setShutdownHandler(this); + out = connector->getOutputHandler(); +} + +void Connection::open( + const std::string& host, int port, + const std::string& uid, const std::string& pwd, const std::string& vhost) +{ + if (isOpen) + THROW_QPID_ERROR(INTERNAL_ERROR, "Channel object is already open"); + connector->connect(host, port); + channels[0] = &channel0; + channel0.open(0, *this); + channel0.protocolInit(uid, pwd, vhost); + isOpen = true; +} + +void Connection::shutdown() { + close(); +} + +void Connection::close( + ReplyCode code, const string& msg, ClassId classId, MethodId methodId +) +{ + if(isOpen) { + // TODO aconway 2007-01-29: Exception handling - could end up + // partly closed with threads left unjoined. + isOpen = false; + channel0.sendAndReceive<ConnectionCloseOkBody>( + new ConnectionCloseBody( + getVersion(), code, msg, classId, methodId)); + + using boost::bind; + for_each(channels.begin(), channels.end(), + bind(&Channel::closeInternal, + bind(&ChannelMap::value_type::second, _1))); + channels.clear(); + connector->close(); + } +} + +void Connection::openChannel(Channel& channel) { + ChannelId id = ++channelIdCounter; + assert (channels.find(id) == channels.end()); + assert(out); + channels[id] = &channel; + channel.open(id, *this); +} + +void Connection::erase(ChannelId id) { + channels.erase(id); +} + +void Connection::received(AMQFrame* frame){ + // FIXME aconway 2007-01-25: Mutex + ChannelId id = frame->getChannel(); + Channel* channel = channels[id]; + // FIXME aconway 2007-01-26: Exception thrown here is hanging the + // client. Need to review use of exceptions. + if (channel == 0) + THROW_QPID_ERROR( + PROTOCOL_ERROR+504, + (boost::format("Invalid channel number %g") % id).str()); + try{ + channel->handleBody(frame->getBody()); + }catch(const qpid::QpidError& e){ + channelException( + *channel, dynamic_cast<AMQMethodBody*>(frame->getBody().get()), e); + } +} + +void Connection::send(AMQFrame* frame) { + out->send(frame); +} + +void Connection::channelException( + Channel& channel, AMQMethodBody* method, const QpidError& e) +{ + int code = (e.code >= PROTOCOL_ERROR) ? e.code - PROTOCOL_ERROR : 500; + string msg = e.msg; + if(method == 0) + channel.close(code, msg); + else + channel.close( + code, msg, method->amqpClassId(), method->amqpMethodId()); +} + +void Connection::idleIn(){ + connector->close(); +} + +void Connection::idleOut(){ + out->send(new AMQFrame(version, 0, new AMQHeartbeatBody())); +} + +}} // namespace qpid::client diff --git a/cpp/lib/client/Connection.h b/cpp/lib/client/Connection.h new file mode 100644 index 0000000000..b4bd311511 --- /dev/null +++ b/cpp/lib/client/Connection.h @@ -0,0 +1,194 @@ +#ifndef _client_Connection_ +#define _client_Connection_ + +/* + * + * 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 "amqp_types.h" +#include <QpidError.h> +#include <Connector.h> +#include <sys/ShutdownHandler.h> +#include <sys/TimeoutHandler.h> + + +#include "framing/amqp_types.h" +#include <framing/amqp_framing.h> +#include <ClientExchange.h> +#include <IncomingMessage.h> +#include <ClientMessage.h> +#include <MessageListener.h> +#include <ClientQueue.h> +#include <ResponseHandler.h> +#include <AMQP_HighestVersion.h> +#include "ClientChannel.h" + +namespace qpid { + +/** + * The client namespace contains all classes that make up a client + * implementation of the AMQP protocol. The key classes that form + * the basis of the client API to be used by applications are + * Connection and Channel. + */ +namespace client { + +class Channel; + +/** + * \internal provide access to selected private channel functions + * for the Connection without making it a friend of the entire channel. + */ +class ConnectionForChannel : + public framing::InputHandler, + public framing::OutputHandler, + public sys::TimeoutHandler, + public sys::ShutdownHandler + +{ + private: + friend class Channel; + virtual void erase(framing::ChannelId) = 0; +}; + + +/** + * \defgroup clientapi Application API for an AMQP client + */ + +/** + * Represents a connection to an AMQP broker. All communication is + * initiated by establishing a connection, then opening one or + * more Channels over that connection. + * + * \ingroup clientapi + */ +class Connection : public ConnectionForChannel +{ + typedef std::map<framing::ChannelId, Channel*> ChannelMap; + + framing::ChannelId channelIdCounter; + static const std::string OK; + + framing::ProtocolVersion version; + const uint32_t max_frame_size; + ChannelMap channels; + Connector defaultConnector; + Connector* connector; + framing::OutputHandler* out; + volatile bool isOpen; + Channel channel0; + bool debug; + + void erase(framing::ChannelId); + void channelException( + Channel&, framing::AMQMethodBody*, const QpidError&); + + // TODO aconway 2007-01-26: too many friendships, untagle these classes. + friend class Channel; + + public: + /** + * Creates a connection object, but does not open the + * connection. + * + * @param _version the version of the protocol to connect with + * + * @param debug turns on tracing for the connection + * (i.e. prints details of the frames sent and received to std + * out). Optional and defaults to false. + * + * @param max_frame_size the maximum frame size that the + * client will accept. Optional and defaults to 65536. + */ + Connection(bool debug = false, uint32_t max_frame_size = 65536, + framing::ProtocolVersion=framing::highestProtocolVersion); + ~Connection(); + + /** + * Opens a connection to a broker. + * + * @param host the host on which the broker is running + * + * @param port the port on the which the broker is listening + * + * @param uid the userid to connect with + * + * @param pwd the password to connect with (currently SASL + * PLAIN is the only authentication method supported so this + * is sent in clear text) + * + * @param virtualhost the AMQP virtual host to use (virtual + * hosts, where implemented(!), provide namespace partitioning + * within a single broker). + */ + void open(const std::string& host, int port = 5672, + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& virtualhost = "/"); + + /** + * Close the connection with optional error information for the peer. + * + * Any further use of this connection (without reopening it) will + * not succeed. + */ + void close(framing::ReplyCode=200, const std::string& msg=OK, + framing::ClassId = 0, framing::MethodId = 0); + + /** + * Associate a Channel with this connection and open it for use. + * + * In AMQP channels are like multi-plexed 'sessions' of work over + * a connection. Almost all the interaction with AMQP is done over + * a channel. + * + * @param connection the connection object to be associated with + * the channel. Call Channel::close() to close the channel. + */ + void openChannel(Channel&); + + + // TODO aconway 2007-01-26: can these be private? + void send(framing::AMQFrame*); + void received(framing::AMQFrame*); + void idleOut(); + void idleIn(); + void shutdown(); + + /**\internal used for testing */ + void setConnector(Connector& connector); + + /** + * @return the maximum frame size in use on this connection + */ + inline uint32_t getMaxFrameSize(){ return max_frame_size; } + + /** @return protocol version in use on this connection. */ + framing::ProtocolVersion getVersion() const { return version; } +}; + +}} // namespace qpid::client + + +#endif diff --git a/cpp/lib/client/Connector.cpp b/cpp/lib/client/Connector.cpp new file mode 100644 index 0000000000..7b73cc016a --- /dev/null +++ b/cpp/lib/client/Connector.cpp @@ -0,0 +1,188 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <QpidError.h> +#include <sys/Time.h> +#include "Connector.h" + +namespace qpid { +namespace client { + +using namespace qpid::sys; +using namespace qpid::framing; +using qpid::QpidError; + +Connector::Connector( + ProtocolVersion ver, bool _debug, uint32_t buffer_size +) : debug(_debug), + receive_buffer_size(buffer_size), + send_buffer_size(buffer_size), + version(ver), + closed(true), + lastIn(0), lastOut(0), + timeout(0), + idleIn(0), idleOut(0), + timeoutHandler(0), + shutdownHandler(0), + inbuf(receive_buffer_size), + outbuf(send_buffer_size) +{ } + +Connector::~Connector(){ } + +void Connector::connect(const std::string& host, int port){ + socket = Socket::createTcp(); + socket.connect(host, port); + closed = false; + receiver = Thread(this); +} + +void Connector::init(){ + ProtocolInitiation init(version); + writeBlock(&init); +} + +void Connector::close(){ + closed = true; + socket.close(); + receiver.join(); +} + +void Connector::setInputHandler(InputHandler* handler){ + input = handler; +} + +void Connector::setShutdownHandler(ShutdownHandler* handler){ + shutdownHandler = handler; +} + +OutputHandler* Connector::getOutputHandler(){ + return this; +} + +void Connector::send(AMQFrame* f){ + std::auto_ptr<AMQFrame> frame(f); + AMQBody::shared_ptr body = frame->getBody(); + writeBlock(frame.get()); + if(debug) std::cout << "SENT: " << *frame << std::endl; +} + +void Connector::writeBlock(AMQDataBlock* data){ + Mutex::ScopedLock l(writeLock); + data->encode(outbuf); + //transfer data to wire + outbuf.flip(); + writeToSocket(outbuf.start(), outbuf.available()); + outbuf.clear(); +} + +void Connector::writeToSocket(char* data, size_t available){ + size_t written = 0; + while(written < available && !closed){ + ssize_t sent = socket.send(data + written, available-written); + if(sent > 0) { + lastOut = now() * TIME_MSEC; + written += sent; + } + } +} + +void Connector::handleClosed(){ + closed = true; + socket.close(); + if(shutdownHandler) shutdownHandler->shutdown(); +} + +void Connector::checkIdle(ssize_t status){ + if(timeoutHandler){ + Time t = now() * TIME_MSEC; + if(status == Socket::SOCKET_TIMEOUT) { + if(idleIn && (t - lastIn > idleIn)){ + timeoutHandler->idleIn(); + } + } + else if(status == 0 || status == Socket::SOCKET_EOF) { + handleClosed(); + } + else { + lastIn = t; + } + if(idleOut && (t - lastOut > idleOut)){ + timeoutHandler->idleOut(); + } + } +} + +void Connector::setReadTimeout(uint16_t t){ + idleIn = t * 1000;//t is in secs + if(idleIn && (!timeout || idleIn < timeout)){ + timeout = idleIn; + setSocketTimeout(); + } + +} + +void Connector::setWriteTimeout(uint16_t t){ + idleOut = t * 1000;//t is in secs + if(idleOut && (!timeout || idleOut < timeout)){ + timeout = idleOut; + setSocketTimeout(); + } +} + +void Connector::setSocketTimeout(){ + socket.setTimeout(timeout*TIME_MSEC); +} + +void Connector::setTimeoutHandler(TimeoutHandler* handler){ + timeoutHandler = handler; +} + +void Connector::run(){ + try{ + while(!closed){ + ssize_t available = inbuf.available(); + if(available < 1){ + THROW_QPID_ERROR(INTERNAL_ERROR, "Frame exceeds buffer size."); + } + ssize_t received = socket.recv(inbuf.start(), available); + checkIdle(received); + + if(!closed && received > 0){ + inbuf.move(received); + inbuf.flip();//position = 0, limit = total data read + + AMQFrame frame(version); + while(frame.decode(inbuf)){ + if(debug) std::cout << "RECV: " << frame << std::endl; + input->received(&frame); + } + //need to compact buffer to preserve any 'extra' data + inbuf.compact(); + } + } + } catch (const std::exception& e) { + std::cout << e.what() << std::endl; + handleClosed(); + } +} + +}} // namespace qpid::client diff --git a/cpp/lib/client/Connector.h b/cpp/lib/client/Connector.h new file mode 100644 index 0000000000..91234d2321 --- /dev/null +++ b/cpp/lib/client/Connector.h @@ -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. + * + */ +#ifndef _Connector_ +#define _Connector_ + + +#include <framing/InputHandler.h> +#include <framing/OutputHandler.h> +#include <framing/InitiationHandler.h> +#include <framing/ProtocolInitiation.h> +#include <ProtocolVersion.h> +#include <sys/ShutdownHandler.h> +#include <sys/TimeoutHandler.h> +#include <sys/Thread.h> +#include <sys/Monitor.h> +#include <sys/Socket.h> + +namespace qpid { + +namespace client { + +class Connector : public framing::OutputHandler, + private sys::Runnable +{ + const bool debug; + const int receive_buffer_size; + const int send_buffer_size; + framing::ProtocolVersion version; + + bool closed; + + int64_t lastIn; + int64_t lastOut; + int64_t timeout; + uint32_t idleIn; + uint32_t idleOut; + + sys::TimeoutHandler* timeoutHandler; + sys::ShutdownHandler* shutdownHandler; + framing::InputHandler* input; + framing::InitiationHandler* initialiser; + framing::OutputHandler* output; + + framing::Buffer inbuf; + framing::Buffer outbuf; + + sys::Mutex writeLock; + sys::Thread receiver; + + sys::Socket socket; + + void checkIdle(ssize_t status); + void writeBlock(framing::AMQDataBlock* data); + void writeToSocket(char* data, size_t available); + void setSocketTimeout(); + + void run(); + void handleClosed(); + + friend class Channel; + public: + Connector(framing::ProtocolVersion pVersion, + bool debug = false, uint32_t buffer_size = 1024); + virtual ~Connector(); + virtual void connect(const std::string& host, int port); + virtual void init(); + virtual void close(); + virtual void setInputHandler(framing::InputHandler* handler); + virtual void setTimeoutHandler(sys::TimeoutHandler* handler); + virtual void setShutdownHandler(sys::ShutdownHandler* handler); + virtual framing::OutputHandler* getOutputHandler(); + virtual void send(framing::AMQFrame* frame); + virtual void setReadTimeout(uint16_t timeout); + virtual void setWriteTimeout(uint16_t timeout); +}; + +}} + + +#endif diff --git a/cpp/lib/client/IncomingMessage.cpp b/cpp/lib/client/IncomingMessage.cpp new file mode 100644 index 0000000000..07f94ceb64 --- /dev/null +++ b/cpp/lib/client/IncomingMessage.cpp @@ -0,0 +1,172 @@ +/* + * + * 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 <IncomingMessage.h> +#include "framing/AMQHeaderBody.h" +#include "framing/AMQContentBody.h" +#include "BasicGetOkBody.h" +#include "BasicReturnBody.h" +#include "BasicDeliverBody.h" +#include <QpidError.h> +#include <iostream> + +namespace qpid { +namespace client { + +using namespace sys; +using namespace framing; + +struct IncomingMessage::Guard: public Mutex::ScopedLock { + Guard(IncomingMessage* im) : Mutex::ScopedLock(im->lock) { + im->shutdownError.throwIf(); + } +}; + +IncomingMessage::IncomingMessage() { reset(); } + +void IncomingMessage::reset() { + state = &IncomingMessage::expectRequest; + endFn= &IncomingMessage::endRequest; + buildMessage = Message(); +} + +void IncomingMessage::startGet() { + Guard g(this); + if (state != &IncomingMessage::expectRequest) { + endGet(new QPID_ERROR(CLIENT_ERROR, "Message already in progress.")); + } + else { + state = &IncomingMessage::expectGetOk; + endFn = &IncomingMessage::endGet; + getError.reset(); + getState = GETTING; + } +} + +bool IncomingMessage::waitGet(Message& msg) { + Guard g(this); + while (getState == GETTING && !shutdownError && !getError) + getReady.wait(lock); + shutdownError.throwIf(); + getError.throwIf(); + msg = getMessage; + return getState==GOT; +} + +Message IncomingMessage::waitDispatch() { + Guard g(this); + while(dispatchQueue.empty() && !shutdownError) + dispatchReady.wait(lock); + shutdownError.throwIf(); + + Message msg(dispatchQueue.front()); + dispatchQueue.pop(); + return msg; +} + +void IncomingMessage::add(BodyPtr body) { + Guard g(this); + shutdownError.throwIf(); + // Call the current state function. + (this->*state)(body); +} + +void IncomingMessage::shutdown() { + Mutex::ScopedLock l(lock); + shutdownError.reset(new ShutdownException()); + getReady.notify(); + dispatchReady.notify(); +} + +bool IncomingMessage::isShutdown() const { + Mutex::ScopedLock l(lock); + return shutdownError; +} + +// Common check for all the expect functions. Called in network thread. +template<class T> +boost::shared_ptr<T> IncomingMessage::expectCheck(BodyPtr body) { + boost::shared_ptr<T> ptr = boost::dynamic_pointer_cast<T>(body); + if (!ptr) + throw QPID_ERROR(PROTOCOL_ERROR+504, "Unexpected frame type"); + return ptr; +} + +void IncomingMessage::expectGetOk(BodyPtr body) { + if (dynamic_cast<BasicGetOkBody*>(body.get())) + state = &IncomingMessage::expectHeader; + else if (dynamic_cast<BasicGetEmptyBody*>(body.get())) { + getState = EMPTY; + endGet(); + } + else + throw QPID_ERROR(PROTOCOL_ERROR+504, "Unexpected frame type"); +} + +void IncomingMessage::expectHeader(BodyPtr body) { + AMQHeaderBody::shared_ptr header = expectCheck<AMQHeaderBody>(body); + buildMessage.header = header; + state = &IncomingMessage::expectContent; + checkComplete(); +} + +void IncomingMessage::expectContent(BodyPtr body) { + AMQContentBody::shared_ptr content = expectCheck<AMQContentBody>(body); + buildMessage.setData(buildMessage.getData() + content->getData()); + checkComplete(); +} + +void IncomingMessage::checkComplete() { + size_t declaredSize = buildMessage.header->getContentSize(); + size_t currentSize = buildMessage.getData().size(); + if (declaredSize == currentSize) + (this->*endFn)(0); + else if (declaredSize < currentSize) + (this->*endFn)(new QPID_ERROR( + PROTOCOL_ERROR, "Message content exceeds declared size.")); +} + +void IncomingMessage::expectRequest(BodyPtr body) { + AMQMethodBody::shared_ptr method = expectCheck<AMQMethodBody>(body); + buildMessage.setMethod(method); + state = &IncomingMessage::expectHeader; +} + +void IncomingMessage::endGet(Exception* ex) { + getError.reset(ex); + if (getState == GETTING) { + getMessage = buildMessage; + getState = GOT; + } + reset(); + getReady.notify(); +} + +void IncomingMessage::endRequest(Exception* ex) { + ExceptionHolder eh(ex); + if (!eh) { + dispatchQueue.push(buildMessage); + reset(); + dispatchReady.notify(); + } + eh.throwIf(); +} + +}} // namespace qpid::client diff --git a/cpp/lib/client/IncomingMessage.h b/cpp/lib/client/IncomingMessage.h new file mode 100644 index 0000000000..2d7c8723c5 --- /dev/null +++ b/cpp/lib/client/IncomingMessage.h @@ -0,0 +1,118 @@ +#ifndef _IncomingMessage_ +#define _IncomingMessage_ + +/* + * + * 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 <string> +#include <queue> +#include <framing/amqp_framing.h> +#include "ExceptionHolder.h" +#include "ClientMessage.h" +#include "sys/Mutex.h" +#include "sys/Condition.h" + +namespace qpid { + +namespace framing { +class AMQBody; +} + +namespace client { +/** + * Accumulates incoming message frames into messages. + * Client-initiated messages (basic.get) are initiated and made + * available to the user thread one at a time. + * + * Broker initiated messages (basic.return, basic.deliver) are + * queued for handling by the user dispatch thread. + */ +class IncomingMessage { + public: + typedef boost::shared_ptr<framing::AMQBody> BodyPtr; + IncomingMessage(); + + /** Expect a new message starting with getOk. Called in user thread.*/ + void startGet(); + + /** Wait for the message to complete, return the message. + * Called in user thread. + *@raises QpidError if there was an error. + */ + bool waitGet(Message&); + + /** Wait for the next broker-initiated message. */ + Message waitDispatch(); + + /** Add a frame body to the message. Called in network thread. */ + void add(BodyPtr); + + /** Shut down: all further calls to any function throw ex. */ + void shutdown(); + + /** Check if shutdown */ + bool isShutdown() const; + + private: + + typedef void (IncomingMessage::* ExpectFn)(BodyPtr); + typedef void (IncomingMessage::* EndFn)(Exception*); + typedef std::queue<Message> MessageQueue; + struct Guard; + friend struct Guard; + + void reset(); + template <class T> boost::shared_ptr<T> expectCheck(BodyPtr); + + // State functions - a state machine where each state is + // a member function that processes a frame body. + void expectGetOk(BodyPtr); + void expectHeader(BodyPtr); + void expectContent(BodyPtr); + void expectRequest(BodyPtr); + + // End functions. + void endGet(Exception* ex = 0); + void endRequest(Exception* ex); + + // Check for complete message. + void checkComplete(); + + mutable sys::Mutex lock; + ExpectFn state; + EndFn endFn; + Message buildMessage; + ExceptionHolder shutdownError; + + // For basic.get messages. + sys::Condition getReady; + ExceptionHolder getError; + Message getMessage; + enum { GETTING, GOT, EMPTY } getState; + + // For broker-initiated messages + sys::Condition dispatchReady; + MessageQueue dispatchQueue; +}; + +}} + + +#endif diff --git a/cpp/lib/client/Makefile.am b/cpp/lib/client/Makefile.am new file mode 100644 index 0000000000..1e10a2a244 --- /dev/null +++ b/cpp/lib/client/Makefile.am @@ -0,0 +1,36 @@ +AM_CXXFLAGS = $(WARNING_CFLAGS) +INCLUDES = \ + -I$(top_srcdir)/gen \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/common/sys \ + -I$(top_srcdir)/lib/common/framing \ + $(APR_CXXFLAGS) + +lib_LTLIBRARIES = libqpidclient.la +libqpidclient_la_LIBADD = ../common/libqpidcommon.la +libqpidclient_la_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO_ARG) +libqpidclient_la_SOURCES = \ + ClientChannel.cpp \ + ClientExchange.cpp \ + ClientMessage.cpp \ + ClientQueue.cpp \ + Basic.cpp \ + Connection.cpp \ + Connector.cpp \ + IncomingMessage.cpp \ + MessageListener.cpp \ + ResponseHandler.cpp \ + ReturnedMessageHandler.cpp +pkginclude_HEADERS = \ + ClientChannel.h \ + ClientExchange.h \ + ClientMessage.h \ + ClientQueue.h \ + Basic.h \ + Connection.h \ + Connector.h \ + IncomingMessage.h \ + MessageListener.h \ + MethodBodyInstances.h \ + ResponseHandler.h \ + ReturnedMessageHandler.h diff --git a/cpp/lib/client/MessageListener.cpp b/cpp/lib/client/MessageListener.cpp new file mode 100644 index 0000000000..70d44e7040 --- /dev/null +++ b/cpp/lib/client/MessageListener.cpp @@ -0,0 +1,24 @@ +/* + * + * 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 <MessageListener.h> + +qpid::client::MessageListener::~MessageListener() {} diff --git a/cpp/lib/client/MessageListener.h b/cpp/lib/client/MessageListener.h new file mode 100644 index 0000000000..cfb917b4f8 --- /dev/null +++ b/cpp/lib/client/MessageListener.h @@ -0,0 +1,49 @@ +/* + * + * 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 <string> + +#ifndef _MessageListener_ +#define _MessageListener_ + +#include <ClientMessage.h> + +namespace qpid { +namespace client { + + /** + * An interface through which asynchronously delivered messages + * can be received by an application. + * + * @see Channel::consume() + * + * \ingroup clientapi + */ + class MessageListener{ + public: + virtual ~MessageListener(); + virtual void received(Message& msg) = 0; + }; + +} +} + + +#endif diff --git a/cpp/lib/client/MethodBodyInstances.h b/cpp/lib/client/MethodBodyInstances.h new file mode 100644 index 0000000000..acbeeb1158 --- /dev/null +++ b/cpp/lib/client/MethodBodyInstances.h @@ -0,0 +1,100 @@ +/* + * + * 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 <framing/amqp_framing.h> + +#ifndef _MethodBodyInstances_h_ +#define _MethodBodyInstances_h_ + +namespace qpid { +namespace client { + +/** + * A list of method body instances that can be used to compare against + * incoming bodies. + */ +class MethodBodyInstances +{ +private: + qpid::framing::ProtocolVersion version; +public: + const qpid::framing::BasicCancelOkBody basic_cancel_ok; + const qpid::framing::BasicConsumeOkBody basic_consume_ok; + const qpid::framing::BasicDeliverBody basic_deliver; + const qpid::framing::BasicGetEmptyBody basic_get_empty; + const qpid::framing::BasicGetOkBody basic_get_ok; + const qpid::framing::BasicQosOkBody basic_qos_ok; + const qpid::framing::BasicReturnBody basic_return; + const qpid::framing::ChannelCloseBody channel_close; + const qpid::framing::ChannelCloseOkBody channel_close_ok; + const qpid::framing::ChannelFlowBody channel_flow; + const qpid::framing::ChannelOpenOkBody channel_open_ok; + const qpid::framing::ConnectionCloseBody connection_close; + const qpid::framing::ConnectionCloseOkBody connection_close_ok; + const qpid::framing::ConnectionOpenOkBody connection_open_ok; + const qpid::framing::ConnectionRedirectBody connection_redirect; + const qpid::framing::ConnectionStartBody connection_start; + const qpid::framing::ConnectionTuneBody connection_tune; + const qpid::framing::ExchangeDeclareOkBody exchange_declare_ok; + const qpid::framing::ExchangeDeleteOkBody exchange_delete_ok; + const qpid::framing::QueueDeclareOkBody queue_declare_ok; + const qpid::framing::QueueDeleteOkBody queue_delete_ok; + const qpid::framing::QueueBindOkBody queue_bind_ok; + const qpid::framing::TxCommitOkBody tx_commit_ok; + const qpid::framing::TxRollbackOkBody tx_rollback_ok; + const qpid::framing::TxSelectOkBody tx_select_ok; + + MethodBodyInstances(uint8_t major, uint8_t minor) : + version(major, minor), + basic_cancel_ok(version), + basic_consume_ok(version), + basic_deliver(version), + basic_get_empty(version), + basic_get_ok(version), + basic_qos_ok(version), + basic_return(version), + channel_close(version), + channel_close_ok(version), + channel_flow(version), + channel_open_ok(version), + connection_close(version), + connection_close_ok(version), + connection_open_ok(version), + connection_redirect(version), + connection_start(version), + connection_tune(version), + exchange_declare_ok(version), + exchange_delete_ok(version), + queue_declare_ok(version), + queue_delete_ok(version), + queue_bind_ok(version), + tx_commit_ok(version), + tx_rollback_ok(version), + tx_select_ok(version) + {} + +}; + +static MethodBodyInstances method_bodies(8, 0); + +} // namespace client +} // namespace qpid + +#endif diff --git a/cpp/lib/client/ResponseHandler.cpp b/cpp/lib/client/ResponseHandler.cpp new file mode 100644 index 0000000000..4498de41ae --- /dev/null +++ b/cpp/lib/client/ResponseHandler.cpp @@ -0,0 +1,86 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/format.hpp> + +#include <ResponseHandler.h> +#include <sys/Monitor.h> +#include <QpidError.h> +#include "amqp_types.h" + +using namespace qpid::sys; +using namespace qpid::framing; + +namespace qpid { +namespace client { + +ResponseHandler::ResponseHandler() : waiting(false){} + +ResponseHandler::~ResponseHandler(){} + +bool ResponseHandler::validate(ClassId c, MethodId m) { + return response != 0 && + response->amqpClassId() ==c && response->amqpMethodId() == m; +} + +void ResponseHandler::waitForResponse(){ + Monitor::ScopedLock l(monitor); + while (waiting) + monitor.wait(); +} + +void ResponseHandler::signalResponse( + qpid::framing::AMQMethodBody::shared_ptr _response) +{ + Monitor::ScopedLock l(monitor); + response = _response; + waiting = false; + monitor.notify(); +} + +void ResponseHandler::receive(ClassId c, MethodId m) { + Monitor::ScopedLock l(monitor); + while (waiting) + monitor.wait(); + getResponse(); // Check for closed. + if(!validate(response->amqpClassId(), response->amqpMethodId())) { + THROW_QPID_ERROR( + PROTOCOL_ERROR, + boost::format("Expected class:method %d:%d, got %d:%d") + % c % m % response->amqpClassId() % response->amqpMethodId()); + } +} + +framing::AMQMethodBody::shared_ptr ResponseHandler::getResponse() { + if (!response) + THROW_QPID_ERROR( + PROTOCOL_ERROR, "Channel closed unexpectedly."); + return response; +} + +RequestId ResponseHandler::getRequestId() { + assert(response->getRequestId()); + return response->getRequestId(); +} +void ResponseHandler::expect(){ + waiting = true; +} + +}} // namespace qpid::client diff --git a/cpp/lib/client/ResponseHandler.h b/cpp/lib/client/ResponseHandler.h new file mode 100644 index 0000000000..d28048c3d3 --- /dev/null +++ b/cpp/lib/client/ResponseHandler.h @@ -0,0 +1,68 @@ +/* + * + * 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 <string> + +#include <framing/amqp_framing.h> // FIXME aconway 2007-02-01: #include cleanup. +#include <sys/Monitor.h> + +#ifndef _ResponseHandler_ +#define _ResponseHandler_ + +namespace qpid { +namespace client { + +/** + * Holds a response from the broker peer for the client. + */ +class ResponseHandler{ + bool waiting; + qpid::framing::AMQMethodBody::shared_ptr response; + qpid::sys::Monitor monitor; + + public: + ResponseHandler(); + ~ResponseHandler(); + + bool isWaiting(){ return waiting; } + framing::AMQMethodBody::shared_ptr getResponse(); + void waitForResponse(); + + void signalResponse(framing::AMQMethodBody::shared_ptr response); + + void expect();//must be called before calling receive + bool validate(framing::ClassId, framing::MethodId); + void receive(framing::ClassId, framing::MethodId); + + framing::RequestId getRequestId(); + + template <class BodyType> bool validate() { + return validate(BodyType::CLASS_ID, BodyType::METHOD_ID); + } + template <class BodyType> void receive() { + receive(BodyType::CLASS_ID, BodyType::METHOD_ID); + } +}; + +} +} + + +#endif diff --git a/cpp/lib/client/ReturnedMessageHandler.cpp b/cpp/lib/client/ReturnedMessageHandler.cpp new file mode 100644 index 0000000000..ee9f7462ef --- /dev/null +++ b/cpp/lib/client/ReturnedMessageHandler.cpp @@ -0,0 +1,24 @@ +/* + * + * 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 <ReturnedMessageHandler.h> + +qpid::client::ReturnedMessageHandler::~ReturnedMessageHandler() {} diff --git a/cpp/lib/client/ReturnedMessageHandler.h b/cpp/lib/client/ReturnedMessageHandler.h new file mode 100644 index 0000000000..137f0b2e17 --- /dev/null +++ b/cpp/lib/client/ReturnedMessageHandler.h @@ -0,0 +1,49 @@ +/* + * + * 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 <string> + +#ifndef _ReturnedMessageHandler_ +#define _ReturnedMessageHandler_ + +#include <ClientMessage.h> + +namespace qpid { +namespace client { + + /** + * An interface through which returned messages can be received by + * an application. + * + * @see Channel::setReturnedMessageHandler() + * + * \ingroup clientapi + */ + class ReturnedMessageHandler{ + public: + virtual ~ReturnedMessageHandler(); + virtual void returned(Message& msg) = 0; + }; + +} +} + + +#endif diff --git a/cpp/lib/common/Exception.cpp b/cpp/lib/common/Exception.cpp new file mode 100644 index 0000000000..f7d91498e0 --- /dev/null +++ b/cpp/lib/common/Exception.cpp @@ -0,0 +1,46 @@ +/* + * + * 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 <Exception.h> + +namespace qpid { + +Exception::Exception() throw() {} + +Exception::Exception(const std::string& str) throw() : whatStr(str) {} + +Exception::Exception(const char* str) throw() : whatStr(str) {} + +Exception::~Exception() throw() {} + +const char* Exception::what() const throw() { return whatStr.c_str(); } + +std::string Exception::toString() const throw() { return whatStr; } + +Exception* Exception::clone() const throw() { return new Exception(*this); } + +void Exception::throwSelf() const { throw *this; } + +ShutdownException::ShutdownException() : Exception("Shut down.") {} + +EmptyException::EmptyException() : Exception("Empty.") {} + +} // namespace qpid diff --git a/cpp/lib/common/Exception.h b/cpp/lib/common/Exception.h new file mode 100644 index 0000000000..b8cd68cf8c --- /dev/null +++ b/cpp/lib/common/Exception.h @@ -0,0 +1,97 @@ +#ifndef _Exception_ +#define _Exception_ + +/* + * + * 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 <exception> +#include <string> +#include <memory> +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> + +#include "amqp_types.h" + +namespace qpid +{ +/** + * Exception base class for all Qpid exceptions. + */ +class Exception : public std::exception +{ + protected: + std::string whatStr; + + public: + Exception() throw(); + Exception(const std::string& str) throw(); + Exception(const char* str) throw(); + Exception(const std::exception&) throw(); + + /** Allow any type that has ostream operator<< to act as message */ + template <class T> + Exception(const T& message) + : whatStr(boost::lexical_cast<std::string>(message)) {} + + virtual ~Exception() throw(); + + virtual const char* what() const throw(); + virtual std::string toString() const throw(); + + virtual Exception* clone() const throw(); + virtual void throwSelf() const; + + typedef boost::shared_ptr<Exception> shared_ptr; +}; + +struct ChannelException : public Exception { + framing::ReplyCode code; + template <class T> + ChannelException(framing::ReplyCode code_, const T& message) + : Exception(message), code(code_) {} + void throwSelf() const { throw *this; } +}; + +struct ConnectionException : public Exception { + framing::ReplyCode code; + template <class T> + ConnectionException(framing::ReplyCode code_, const T& message) + : Exception(message), code(code_) {} + void throwSelf() const { throw *this; } +}; + +/** + * Exception used to indicate that a thread should shut down. + * Does not indicate an error that should be signalled to the user. + */ +struct ShutdownException : public Exception { + ShutdownException(); + void throwSelf() const { throw *this; } +}; + +/** Exception to indicate empty queue or other empty state */ +struct EmptyException : public Exception { + EmptyException(); + void throwSelf() const { throw *this; } +}; + +} + +#endif /*!_Exception_*/ diff --git a/cpp/lib/common/ExceptionHolder.cpp b/cpp/lib/common/ExceptionHolder.cpp new file mode 100644 index 0000000000..de8d7b2487 --- /dev/null +++ b/cpp/lib/common/ExceptionHolder.cpp @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "ExceptionHolder.h" + +namespace qpid { + +ExceptionHolder::ExceptionHolder(const std::exception& e) { + const Exception* ex = dynamic_cast<const Exception*>(&e); + if (ex) { + reset(ex->clone()); + } else { + reset(new Exception(e.what())); + } +} + +} diff --git a/cpp/lib/common/ExceptionHolder.h b/cpp/lib/common/ExceptionHolder.h new file mode 100644 index 0000000000..2769455aba --- /dev/null +++ b/cpp/lib/common/ExceptionHolder.h @@ -0,0 +1,67 @@ +#ifndef _qpid_ExceptionHolder_h +#define _qpid_ExceptionHolder_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <assert.h> +#include <Exception.h> +#include <boost/shared_ptr.hpp> + +namespace qpid { + +// FIXME aconway 2007-02-20: Not necessary, a simple +// Exception::shared_ptr will do the job. Remove +// +/** + * Holder for a heap-allocated exc eption that can be stack allocated + * and thrown safely. + * + * Basically this is a shared_ptr with the Exception functions added + * so the catcher need not be aware that it is a pointer rather than a + * reference. + * + * shared_ptr is chosen over auto_ptr because it has normal + * copy semantics. + */ +class ExceptionHolder : public Exception, public boost::shared_ptr<Exception> +{ + public: + typedef boost::shared_ptr<Exception> shared_ptr; + + ExceptionHolder() throw() {} + ExceptionHolder(Exception* p) throw() : shared_ptr(p) {} + ExceptionHolder(shared_ptr p) throw() : shared_ptr(p) {} + + ExceptionHolder(const Exception& e) throw() : shared_ptr(e.clone()) {} + ExceptionHolder(const std::exception& e); + + ~ExceptionHolder() throw() {} + + const char* what() const throw() { return get()->what(); } + std::string toString() const throw() { return get()->toString(); } + Exception* clone() const throw() { return get()->clone(); } + void throwIf() const { if (get()) get()->throwSelf(); } + void throwSelf() const { assert(get()); get()->throwSelf(); } +}; + +} // namespace qpid + + + +#endif /*!_qpid_ExceptionHolder_h*/ diff --git a/cpp/lib/common/Makefile.am b/cpp/lib/common/Makefile.am new file mode 100644 index 0000000000..d70adf1186 --- /dev/null +++ b/cpp/lib/common/Makefile.am @@ -0,0 +1,142 @@ +AM_CXXFLAGS = $(WARNING_CFLAGS) + +INCLUDES = \ + -I$(top_srcdir)/gen \ + -I$(top_srcdir)/lib/common/sys \ + -I$(top_srcdir)/lib/common/framing \ + $(APR_CXXFLAGS) + +apr = sys/apr +apr_src = \ + $(apr)/APRAcceptor.cpp \ + $(apr)/APRBase.cpp \ + $(apr)/APRPool.cpp \ + $(apr)/APRSocket.cpp \ + $(apr)/LFProcessor.cpp \ + $(apr)/LFSessionContext.cpp \ + $(apr)/Socket.cpp \ + $(apr)/Thread.cpp +apr_hdr = \ + $(apr)/APRBase.h \ + $(apr)/APRPool.h \ + $(apr)/APRSocket.h \ + $(apr)/LFProcessor.h \ + $(apr)/LFSessionContext.h + +posix = sys/posix +posix_src = \ + $(posix)/PosixAcceptor.cpp \ + $(posix)/Socket.cpp \ + $(posix)/Thread.cpp \ + $(posix)/check.cpp \ + $(posix)/EventChannel.cpp \ + $(posix)/EventChannelThreads.cpp +posix_hdr = \ + $(posix)/check.h \ + $(posix)/EventChannel.h \ + $(posix)/EventChannelThreads.h + +EXTRA_DIST=$(posix_src) $(posix_hdr) +platform_src = $(apr_src) +platform_hdr = $(apr_hdr) + +framing = framing +gen = $(srcdir)/../../gen + +lib_LTLIBRARIES = libqpidcommon.la +libqpidcommon_la_LIBADD = \ + $(APR_LIBS) \ + $(LIB_DLOPEN) \ + $(LIB_CLOCK_GETTIME) + +libqpidcommon_la_LDFLAGS = \ + -version-info \ + $(LIBTOOL_VERSION_INFO_ARG) + +libqpidcommon_la_SOURCES = \ + $(platform_src) \ + $(framing)/AMQBody.cpp \ + $(framing)/AMQRequestBody.cpp \ + $(framing)/AMQResponseBody.cpp \ + $(framing)/AMQContentBody.cpp \ + $(framing)/AMQFrame.cpp \ + $(framing)/AMQHeaderBody.cpp \ + $(framing)/AMQHeartbeatBody.cpp \ + $(framing)/AMQMethodBody.cpp \ + $(framing)/MethodContext.cpp \ + $(framing)/BasicHeaderProperties.cpp \ + $(framing)/BodyHandler.cpp \ + $(framing)/ChannelAdapter.cpp \ + $(framing)/Buffer.cpp \ + $(framing)/FieldTable.cpp \ + $(framing)/FramingContent.cpp \ + $(framing)/InitiationHandler.cpp \ + $(framing)/ProtocolInitiation.cpp \ + $(framing)/ProtocolVersion.cpp \ + $(framing)/ProtocolVersionException.cpp \ + $(framing)/Requester.cpp \ + $(framing)/Responder.cpp \ + $(framing)/Value.cpp \ + $(framing)/Proxy.cpp \ + $(gen)/AMQP_ClientProxy.cpp \ + $(gen)/AMQP_HighestVersion.h \ + $(gen)/AMQP_MethodVersionMap.cpp \ + $(gen)/AMQP_ServerProxy.cpp \ + Exception.cpp \ + ExceptionHolder.cpp \ + QpidError.cpp \ + sys/Runnable.cpp \ + sys/Time.cpp \ + sys/ProducerConsumer.cpp + +nobase_pkginclude_HEADERS = \ + $(gen)/AMQP_HighestVersion.h \ + $(platform_hdr) \ + $(framing)/AMQBody.h \ + $(framing)/AMQContentBody.h \ + $(framing)/AMQDataBlock.h \ + $(framing)/AMQFrame.h \ + $(framing)/AMQHeaderBody.h \ + $(framing)/AMQHeartbeatBody.h \ + $(framing)/AMQMethodBody.h \ + $(framing)/MethodContext.h \ + $(framing)/BasicHeaderProperties.h \ + $(framing)/BodyHandler.h \ + $(framing)/ChannelAdapter.h \ + $(framing)/Buffer.h \ + $(framing)/FieldTable.h \ + $(framing)/FramingContent.h \ + $(framing)/HeaderProperties.h \ + $(framing)/InitiationHandler.h \ + $(framing)/InputHandler.h \ + $(framing)/OutputHandler.h \ + $(framing)/ProtocolInitiation.h \ + $(framing)/ProtocolVersion.h \ + $(framing)/ProtocolVersionException.h \ + $(framing)/Value.h \ + $(framing)/amqp_framing.h \ + $(framing)/amqp_types.h \ + $(framing)/Proxy.h \ + Exception.h \ + ExceptionHolder.h \ + QpidError.h \ + SharedObject.h \ + sys/Acceptor.h \ + sys/AtomicCount.h \ + sys/Module.h \ + sys/Monitor.h \ + sys/Mutex.h \ + sys/Runnable.h \ + sys/ConnectionOutputHandler.h \ + sys/ConnectionInputHandler.h \ + sys/ConnectionInputHandlerFactory.h \ + sys/ShutdownHandler.h \ + sys/Socket.h \ + sys/Thread.h \ + sys/Time.h \ + sys/TimeoutHandler.h \ + sys/ProducerConsumer.h + + +# Force build during dist phase so help2man will work. +dist-hook: $(lib_LTLIBRARIES) diff --git a/cpp/lib/common/QpidError.cpp b/cpp/lib/common/QpidError.cpp new file mode 100644 index 0000000000..9cd3051d1e --- /dev/null +++ b/cpp/lib/common/QpidError.cpp @@ -0,0 +1,42 @@ +/* + * + * 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/format.hpp> + +#include <QpidError.h> +#include <sstream> + +using namespace qpid; + +QpidError::QpidError() : code(0) {} + +QpidError::~QpidError() throw() {} + +Exception* QpidError::clone() const throw() { return new QpidError(*this); } + +void QpidError::throwSelf() const { throw *this; } + +void QpidError::init() { + whatStr = boost::str(boost::format("Error [%d] %s (%s:%d)") + % code % msg % loc.file % loc.line); +} + + diff --git a/cpp/lib/common/QpidError.h b/cpp/lib/common/QpidError.h new file mode 100644 index 0000000000..2a0395ab79 --- /dev/null +++ b/cpp/lib/common/QpidError.h @@ -0,0 +1,78 @@ +#ifndef __QpidError__ +#define __QpidError__ +/* + * + * 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 <string> +#include <memory> +#include <ostream> + +#include <Exception.h> + +namespace qpid { + +struct SrcLine { + public: + SrcLine(const std::string& file_="", int line_=0) : + file(file_), line(line_) {} + + std::string file; + int line; +}; + +class QpidError : public Exception +{ + public: + const int code; + SrcLine loc; + std::string msg; + + QpidError(); + + template <class T> + QpidError(int code_, const T& msg_, const SrcLine& loc_) throw() + : code(code_), loc(loc_), msg(boost::lexical_cast<std::string>(msg_)) + { init(); } + + ~QpidError() throw(); + Exception* clone() const throw(); + void throwSelf() const; + + private: + + void init(); +}; + + +} // namespace qpid + +#define SRCLINE ::qpid::SrcLine(__FILE__, __LINE__) + +#define QPID_ERROR(CODE, MESSAGE) ::qpid::QpidError((CODE), (MESSAGE), SRCLINE) + +#define THROW_QPID_ERROR(CODE, MESSAGE) throw QPID_ERROR(CODE,MESSAGE) + +const int PROTOCOL_ERROR = 10000; +const int APR_ERROR = 20000; +const int FRAMING_ERROR = 30000; +const int CLIENT_ERROR = 40000; +const int INTERNAL_ERROR = 50000; + +#endif diff --git a/cpp/lib/common/SharedObject.h b/cpp/lib/common/SharedObject.h new file mode 100644 index 0000000000..852a036ab9 --- /dev/null +++ b/cpp/lib/common/SharedObject.h @@ -0,0 +1,55 @@ +#ifndef _SharedObject_ +#define _SharedObject_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +namespace qpid { + /** + * Template to enforce shared object conventions. + * Shared object classes should inherit : public qpid::SharedObject + * That ensures Foo: + * - has typedef boost::shared_ptr<T> shared_ptr + * - has virtual destructor + * - is boost::noncopyable (no default copy or assign) + * - has a protected default constructor. + * + * Shared objects should not have public constructors. + * Make constructors protected and provide public statc create() + * functions that return a shared_ptr. + */ + template <class T> + class SharedObject : private boost::noncopyable + { + public: + typedef boost::shared_ptr<T> shared_ptr; + + virtual ~SharedObject() {}; + + protected: + SharedObject() {} + }; +} + +#endif /*!_SharedObject_*/ diff --git a/cpp/lib/common/doxygen_mainpage.h b/cpp/lib/common/doxygen_mainpage.h new file mode 100644 index 0000000000..b354238cd0 --- /dev/null +++ b/cpp/lib/common/doxygen_mainpage.h @@ -0,0 +1,45 @@ +// This header file is just for doxygen documentation purposes. + +/*!\mainpage Qpid C++ Developer Kit. + * + *\section intro_sec Introduction + * + * The <a href=http://incubator.apache.org/qpid/index.html>Qpid project</a> provides implementations of the <a href="http://amqp.org/">AMQP messaging specification</a> in several programming language. + * + * Qpidc provides APIs and libraries to implement AMQP + * clients in C++. Qpidc clients can interact with any compliant AMQP + * message broker. The Qpid project also provides an AMQP broker + * daemon called qpidd that you can use with your qpidc clients. + * + *\section install_sec Installation + * + * If you are installing from the source distribution + <pre> + > ./configure && make + > make install </pre> + * This will build and install the client development kit and the broker + * in standard places. Use + * <code>./configure --help</code> for more options. + * + * You can also install from RPMs with the <code>rpm -i</code> command. + * You will need + * - <code>qpidc</code> for core libraries. + * - <code>qpidc-devel</code> for header files and developer documentation. + * - <code>qpidd</code> for the broker daemon. + * + *\section getstart_sec Getting Started + * + * If you have installed in the standard places you should use + * these compile flags: + * + *<code> -I/usr/include/qpidc -I/usr/include/qpidc/framing -I/usr/include/qpidc/sys</code> + * + * and these link flags: + * + *<code> -lqpidcommon -lqpidclient</code> + * + * If you have installed somewhere else you should modify the flags + * appropriately. + * + * See the \ref clientapi "client API module" for more on the client API. + */ diff --git a/cpp/lib/common/framing/AMQBody.cpp b/cpp/lib/common/framing/AMQBody.cpp new file mode 100644 index 0000000000..c7c253beda --- /dev/null +++ b/cpp/lib/common/framing/AMQBody.cpp @@ -0,0 +1,33 @@ +/* + * + * 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 <AMQBody.h> +#include <iostream> + +std::ostream& qpid::framing::operator<<(std::ostream& out, const qpid::framing::AMQBody& body) +{ + body.print(out); + return out; +} + +qpid::framing::AMQBody::~AMQBody() {} + + diff --git a/cpp/lib/common/framing/AMQBody.h b/cpp/lib/common/framing/AMQBody.h new file mode 100644 index 0000000000..26076956ca --- /dev/null +++ b/cpp/lib/common/framing/AMQBody.h @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> +#include <amqp_types.h> +#include <Buffer.h> + +#ifndef _AMQBody_ +#define _AMQBody_ + +namespace qpid { + namespace framing { + + class AMQBody + { + public: + typedef boost::shared_ptr<AMQBody> shared_ptr; + + virtual ~AMQBody(); + virtual uint32_t size() const = 0; + virtual uint8_t type() const = 0; + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, uint32_t size) = 0; + + virtual void print(std::ostream& out) const = 0; + }; + + std::ostream& operator<<(std::ostream& out, const AMQBody& body) ; + + enum BodyTypes { + METHOD_BODY = 1, + HEADER_BODY = 2, + CONTENT_BODY = 3, + HEARTBEAT_BODY = 8, + REQUEST_BODY = 9, + RESPONSE_BODY = 10 + }; + } +} + + +#endif diff --git a/cpp/lib/common/framing/AMQContentBody.cpp b/cpp/lib/common/framing/AMQContentBody.cpp new file mode 100644 index 0000000000..573c17dade --- /dev/null +++ b/cpp/lib/common/framing/AMQContentBody.cpp @@ -0,0 +1,43 @@ +/* + * + * 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 <AMQContentBody.h> +#include <iostream> + +qpid::framing::AMQContentBody::AMQContentBody(){ +} + +qpid::framing::AMQContentBody::AMQContentBody(const string& _data) : data(_data){ +} + +uint32_t qpid::framing::AMQContentBody::size() const{ + return data.size(); +} +void qpid::framing::AMQContentBody::encode(Buffer& buffer) const{ + buffer.putRawData(data); +} +void qpid::framing::AMQContentBody::decode(Buffer& buffer, uint32_t _size){ + buffer.getRawData(data, _size); +} + +void qpid::framing::AMQContentBody::print(std::ostream& out) const +{ + out << "content (" << size() << " bytes)"; +} diff --git a/cpp/lib/common/framing/AMQContentBody.h b/cpp/lib/common/framing/AMQContentBody.h new file mode 100644 index 0000000000..c9fa7cde5c --- /dev/null +++ b/cpp/lib/common/framing/AMQContentBody.h @@ -0,0 +1,53 @@ +/* + * + * 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 <amqp_types.h> +#include <AMQBody.h> +#include <Buffer.h> + +#ifndef _AMQContentBody_ +#define _AMQContentBody_ + +namespace qpid { +namespace framing { + +class AMQContentBody : public AMQBody +{ + string data; + +public: + typedef boost::shared_ptr<AMQContentBody> shared_ptr; + + AMQContentBody(); + AMQContentBody(const string& data); + inline virtual ~AMQContentBody(){} + inline uint8_t type() const { return CONTENT_BODY; }; + inline string& getData(){ return data; } + uint32_t size() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, uint32_t size); + void print(std::ostream& out) const; +}; + +} +} + + +#endif diff --git a/cpp/lib/common/framing/AMQDataBlock.h b/cpp/lib/common/framing/AMQDataBlock.h new file mode 100644 index 0000000000..2a6f843f1e --- /dev/null +++ b/cpp/lib/common/framing/AMQDataBlock.h @@ -0,0 +1,42 @@ +/* + * + * 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 <Buffer.h> + +#ifndef _AMQDataBlock_ +#define _AMQDataBlock_ + +namespace qpid { +namespace framing { + +class AMQDataBlock +{ +public: + virtual ~AMQDataBlock() {} + virtual void encode(Buffer& buffer) = 0; + virtual bool decode(Buffer& buffer) = 0; + virtual uint32_t size() const = 0; +}; + +} +} + + +#endif diff --git a/cpp/lib/common/framing/AMQFrame.cpp b/cpp/lib/common/framing/AMQFrame.cpp new file mode 100644 index 0000000000..bc9061b169 --- /dev/null +++ b/cpp/lib/common/framing/AMQFrame.cpp @@ -0,0 +1,139 @@ + +/* + * + * 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/format.hpp> + +#include <AMQFrame.h> +#include <QpidError.h> +#include "AMQRequestBody.h" +#include "AMQResponseBody.h" + + +namespace qpid { +namespace framing { + + +AMQP_MethodVersionMap AMQFrame::versionMap; + +AMQFrame::AMQFrame(ProtocolVersion _version): +version(_version) + { + assert(version != ProtocolVersion(0,0)); + } + +AMQFrame::AMQFrame(ProtocolVersion _version, uint16_t _channel, AMQBody* _body) : +version(_version), channel(_channel), body(_body) +{} + +AMQFrame::AMQFrame(ProtocolVersion _version, uint16_t _channel, const AMQBody::shared_ptr& _body) : +version(_version), channel(_channel), body(_body) +{} + +AMQFrame::~AMQFrame() {} + +uint16_t AMQFrame::getChannel(){ + return channel; +} + +AMQBody::shared_ptr AMQFrame::getBody(){ + return body; +} + +void AMQFrame::encode(Buffer& buffer) +{ + buffer.putOctet(body->type()); + buffer.putShort(channel); + buffer.putLong(body->size()); + body->encode(buffer); + buffer.putOctet(0xCE); +} + +uint32_t AMQFrame::size() const{ + assert(body.get()); + return 1/*type*/ + 2/*channel*/ + 4/*body size*/ + body->size() + + 1/*0xCE*/; +} + +bool AMQFrame::decode(Buffer& buffer) +{ + if(buffer.available() < 7) + return false; + buffer.record(); + uint32_t frameSize = decodeHead(buffer); + if(buffer.available() < frameSize + 1){ + buffer.restore(); + return false; + } + decodeBody(buffer, frameSize); + uint8_t end = buffer.getOctet(); + if(end != 0xCE) THROW_QPID_ERROR(FRAMING_ERROR, "Frame end not found"); + return true; +} + +uint32_t AMQFrame::decodeHead(Buffer& buffer){ + type = buffer.getOctet(); + channel = buffer.getShort(); + return buffer.getLong(); +} + +void AMQFrame::decodeBody(Buffer& buffer, uint32_t size) +{ + switch(type) + { + case METHOD_BODY: + body = AMQMethodBody::create(versionMap, version, buffer); + break; + case REQUEST_BODY: + body = AMQRequestBody::create(versionMap, version, buffer); + break; + case RESPONSE_BODY: + body = AMQResponseBody::create(versionMap, version, buffer); + break; + case HEADER_BODY: + body = AMQBody::shared_ptr(new AMQHeaderBody()); + break; + case CONTENT_BODY: + body = AMQBody::shared_ptr(new AMQContentBody()); + break; + case HEARTBEAT_BODY: + body = AMQBody::shared_ptr(new AMQHeartbeatBody()); + break; + default: + THROW_QPID_ERROR( + FRAMING_ERROR, + boost::format("Unknown frame type %d") % type); + } + body->decode(buffer, size); +} + +std::ostream& operator<<(std::ostream& out, const AMQFrame& t) +{ + out << "Frame[channel=" << t.channel << "; "; + if (t.body.get() == 0) + out << "empty"; + else + out << *t.body; + out << "]"; + return out; +} + + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/AMQFrame.h b/cpp/lib/common/framing/AMQFrame.h new file mode 100644 index 0000000000..0c18e0c2a5 --- /dev/null +++ b/cpp/lib/common/framing/AMQFrame.h @@ -0,0 +1,78 @@ +#ifndef _AMQFrame_ +#define _AMQFrame_ + +/* + * + * 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/cast.hpp> + +#include <amqp_types.h> +#include <AMQBody.h> +#include <AMQDataBlock.h> +#include <AMQMethodBody.h> +#include <AMQHeaderBody.h> +#include <AMQContentBody.h> +#include <AMQHeartbeatBody.h> +#include <AMQP_MethodVersionMap.h> +#include <AMQP_HighestVersion.h> +#include <Buffer.h> + +namespace qpid { +namespace framing { + + +class AMQFrame : public AMQDataBlock +{ + public: + AMQFrame(ProtocolVersion _version = highestProtocolVersion); + AMQFrame(ProtocolVersion _version, uint16_t channel, AMQBody* body); + AMQFrame(ProtocolVersion _version, uint16_t channel, const AMQBody::shared_ptr& body); + virtual ~AMQFrame(); + virtual void encode(Buffer& buffer); + virtual bool decode(Buffer& buffer); + virtual uint32_t size() const; + uint16_t getChannel(); + AMQBody::shared_ptr getBody(); + + /** Convenience template to cast the body to an expected type */ + template <class T> boost::shared_ptr<T> castBody() { + assert(dynamic_cast<T*>(getBody().get())); + boost::static_pointer_cast<T>(getBody()); + } + + uint32_t decodeHead(Buffer& buffer); + void decodeBody(Buffer& buffer, uint32_t size); + + private: + static AMQP_MethodVersionMap versionMap; + ProtocolVersion version; + + uint16_t channel; + uint8_t type; + AMQBody::shared_ptr body; + + + friend std::ostream& operator<<(std::ostream& out, const AMQFrame& body); +}; + +}} // namespace qpid::framing + + +#endif diff --git a/cpp/lib/common/framing/AMQHeaderBody.cpp b/cpp/lib/common/framing/AMQHeaderBody.cpp new file mode 100644 index 0000000000..3ddae4eebf --- /dev/null +++ b/cpp/lib/common/framing/AMQHeaderBody.cpp @@ -0,0 +1,75 @@ +/* + * + * 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 <AMQHeaderBody.h> +#include <QpidError.h> +#include <BasicHeaderProperties.h> + +qpid::framing::AMQHeaderBody::AMQHeaderBody(int classId) : weight(0), contentSize(0){ + createProperties(classId); +} + +qpid::framing::AMQHeaderBody::AMQHeaderBody() : properties(0), weight(0), contentSize(0){ +} + +qpid::framing::AMQHeaderBody::~AMQHeaderBody(){ + delete properties; +} + +uint32_t qpid::framing::AMQHeaderBody::size() const{ + return 12 + properties->size(); +} + +void qpid::framing::AMQHeaderBody::encode(Buffer& buffer) const { + buffer.putShort(properties->classId()); + buffer.putShort(weight); + buffer.putLongLong(contentSize); + properties->encode(buffer); +} + +void qpid::framing::AMQHeaderBody::decode(Buffer& buffer, uint32_t bufSize){ + uint16_t classId = buffer.getShort(); + weight = buffer.getShort(); + contentSize = buffer.getLongLong(); + createProperties(classId); + properties->decode(buffer, bufSize - 12); +} + +void qpid::framing::AMQHeaderBody::createProperties(int classId){ + switch(classId){ + case BASIC: + properties = new qpid::framing::BasicHeaderProperties(); + break; + default: + THROW_QPID_ERROR(FRAMING_ERROR, "Unknown header class"); + } +} + +void qpid::framing::AMQHeaderBody::print(std::ostream& out) const +{ + out << "header (" << size() << " bytes)" << " content_size=" << getContentSize(); + const BasicHeaderProperties* props = + dynamic_cast<const BasicHeaderProperties*>(getProperties()); + if (props) { + out << ", message_id=" << props->getMessageId(); + out << ", delivery_mode=" << (int) props->getDeliveryMode(); + out << ", headers=" << const_cast<BasicHeaderProperties*>(props)->getHeaders(); + } +} diff --git a/cpp/lib/common/framing/AMQHeaderBody.h b/cpp/lib/common/framing/AMQHeaderBody.h new file mode 100644 index 0000000000..d57f93aacd --- /dev/null +++ b/cpp/lib/common/framing/AMQHeaderBody.h @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <amqp_types.h> +#include <AMQBody.h> +#include <Buffer.h> +#include <HeaderProperties.h> + +#ifndef _AMQHeaderBody_ +#define _AMQHeaderBody_ + +namespace qpid { +namespace framing { + +class AMQHeaderBody : public AMQBody +{ + HeaderProperties* properties; + uint16_t weight; + uint64_t contentSize; + + void createProperties(int classId); +public: + typedef boost::shared_ptr<AMQHeaderBody> shared_ptr; + + AMQHeaderBody(int classId); + AMQHeaderBody(); + inline uint8_t type() const { return HEADER_BODY; } + HeaderProperties* getProperties(){ return properties; } + const HeaderProperties* getProperties() const { return properties; } + inline uint64_t getContentSize() const { return contentSize; } + inline void setContentSize(uint64_t _size) { contentSize = _size; } + virtual ~AMQHeaderBody(); + virtual uint32_t size() const; + virtual void encode(Buffer& buffer) const; + virtual void decode(Buffer& buffer, uint32_t size); + virtual void print(std::ostream& out) const; +}; + +} +} + + +#endif diff --git a/cpp/lib/common/framing/AMQHeartbeatBody.cpp b/cpp/lib/common/framing/AMQHeartbeatBody.cpp new file mode 100644 index 0000000000..63f83a3d29 --- /dev/null +++ b/cpp/lib/common/framing/AMQHeartbeatBody.cpp @@ -0,0 +1,29 @@ +/* + * + * 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 <AMQHeartbeatBody.h> +#include <iostream> + +qpid::framing::AMQHeartbeatBody::~AMQHeartbeatBody() {} + +void qpid::framing::AMQHeartbeatBody::print(std::ostream& out) const { + out << "heartbeat"; +} diff --git a/cpp/lib/common/framing/AMQHeartbeatBody.h b/cpp/lib/common/framing/AMQHeartbeatBody.h new file mode 100644 index 0000000000..a3e9d823f1 --- /dev/null +++ b/cpp/lib/common/framing/AMQHeartbeatBody.h @@ -0,0 +1,47 @@ +/* + * + * 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 <amqp_types.h> +#include <AMQBody.h> +#include <Buffer.h> + +#ifndef _AMQHeartbeatBody_ +#define _AMQHeartbeatBody_ + +namespace qpid { +namespace framing { + +class AMQHeartbeatBody : public AMQBody +{ +public: + typedef boost::shared_ptr<AMQHeartbeatBody> shared_ptr; + + virtual ~AMQHeartbeatBody(); + inline uint32_t size() const { return 0; } + inline uint8_t type() const { return HEARTBEAT_BODY; } + inline void encode(Buffer& ) const {} + inline void decode(Buffer& , uint32_t /*size*/) {} + virtual void print(std::ostream& out) const; +}; + +} +} + +#endif diff --git a/cpp/lib/common/framing/AMQMethodBody.cpp b/cpp/lib/common/framing/AMQMethodBody.cpp new file mode 100644 index 0000000000..23502068f5 --- /dev/null +++ b/cpp/lib/common/framing/AMQMethodBody.cpp @@ -0,0 +1,59 @@ +/* + * + * 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 <AMQFrame.h> +#include <AMQMethodBody.h> +#include <QpidError.h> +#include "AMQP_MethodVersionMap.h" + +namespace qpid { +namespace framing { + +void AMQMethodBody::encodeId(Buffer& buffer) const{ + buffer.putShort(amqpClassId()); + buffer.putShort(amqpMethodId()); +} + +void AMQMethodBody::invoke(AMQP_ServerOperations&, const MethodContext&){ + assert(0); + THROW_QPID_ERROR(PROTOCOL_ERROR, "Method not supported by AMQP Server."); +} + +AMQMethodBody::shared_ptr AMQMethodBody::create( + AMQP_MethodVersionMap& versionMap, ProtocolVersion version, + Buffer& buffer) +{ + ClassMethodId id; + id.decode(buffer); + return AMQMethodBody::shared_ptr( + versionMap.createMethodBody( + id.classId, id.methodId, version.getMajor(), version.getMinor())); +} + +void AMQMethodBody::ClassMethodId::decode(Buffer& buffer) { + classId = buffer.getShort(); + methodId = buffer.getShort(); +} + +void AMQMethodBody::decode(Buffer& buffer, uint32_t /*size*/) { + decodeContent(buffer); +} + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/AMQMethodBody.h b/cpp/lib/common/framing/AMQMethodBody.h new file mode 100644 index 0000000000..c2b00c2169 --- /dev/null +++ b/cpp/lib/common/framing/AMQMethodBody.h @@ -0,0 +1,84 @@ +#ifndef _AMQMethodBody_ +#define _AMQMethodBody_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <amqp_types.h> +#include <AMQBody.h> +#include <Buffer.h> +#include <AMQP_ServerOperations.h> +#include <MethodContext.h> + +namespace qpid { +namespace framing { + +class AMQP_MethodVersionMap; + +class AMQMethodBody : public AMQBody +{ + public: + typedef boost::shared_ptr<AMQMethodBody> shared_ptr; + + static shared_ptr create( + AMQP_MethodVersionMap& map, ProtocolVersion version, Buffer& buf); + + ProtocolVersion version; + uint8_t type() const { return METHOD_BODY; } + AMQMethodBody(uint8_t major, uint8_t minor) : version(major, minor) {} + AMQMethodBody(ProtocolVersion ver) : version(ver) {} + virtual ~AMQMethodBody() {} + void decode(Buffer&, uint32_t); + + virtual MethodId amqpMethodId() const = 0; + virtual ClassId amqpClassId() const = 0; + + virtual void invoke(AMQP_ServerOperations&, const MethodContext&); + + template <class T> bool isA() { + return amqpClassId()==T::CLASS_ID && amqpMethodId()==T::METHOD_ID; + } + + /** Return request ID or response correlationID */ + virtual RequestId getRequestId() const { return 0; } + + virtual bool isRequest() const { return false; } + virtual bool isResponse() const { return false; } + + protected: + static uint32_t baseSize() { return 4; } + + struct ClassMethodId { + uint16_t classId; + uint16_t methodId; + void decode(Buffer& b); + }; + + void encodeId(Buffer& buffer) const; + virtual void encodeContent(Buffer& buffer) const = 0; + virtual void decodeContent(Buffer& buffer) = 0; +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/cpp/lib/common/framing/AMQRequestBody.cpp b/cpp/lib/common/framing/AMQRequestBody.cpp new file mode 100644 index 0000000000..54e1c11863 --- /dev/null +++ b/cpp/lib/common/framing/AMQRequestBody.cpp @@ -0,0 +1,66 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "AMQRequestBody.h" +#include "AMQP_MethodVersionMap.h" + +namespace qpid { +namespace framing { + +void AMQRequestBody::Data::encode(Buffer& buffer) const { + buffer.putLongLong(requestId); + buffer.putLongLong(responseMark); + buffer.putLong(0); // Reserved long in spec. +} + +void AMQRequestBody::Data::decode(Buffer& buffer) { + requestId = buffer.getLongLong(); + responseMark = buffer.getLongLong(); + buffer.getLong(); // Ignore reserved long. +} + +void AMQRequestBody::encode(Buffer& buffer) const { + data.encode(buffer); + encodeId(buffer); + encodeContent(buffer); +} + +AMQRequestBody::shared_ptr +AMQRequestBody::create( + AMQP_MethodVersionMap& versionMap, ProtocolVersion version, + Buffer& buffer) +{ + ClassMethodId id; + Data data; + data.decode(buffer); + id.decode(buffer); + AMQRequestBody* body = dynamic_cast<AMQRequestBody*>( + versionMap.createMethodBody( + id.classId, id.methodId, version.getMajor(), version.getMinor())); + assert(body); + body->data = data; + return AMQRequestBody::shared_ptr(body); +} + +void AMQRequestBody::printPrefix(std::ostream& out) const { + out << "request(id=" << data.requestId << ",mark=" + << data.responseMark << "): "; +} + +}} // namespace qpid::framing + diff --git a/cpp/lib/common/framing/AMQRequestBody.h b/cpp/lib/common/framing/AMQRequestBody.h new file mode 100644 index 0000000000..e184fff1d6 --- /dev/null +++ b/cpp/lib/common/framing/AMQRequestBody.h @@ -0,0 +1,78 @@ +#ifndef _framing_AMQRequestBody_h +#define _framing_AMQRequestBody_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "AMQMethodBody.h" + +namespace qpid { +namespace framing { + +/** + * Body of a request method frame. + */ +class AMQRequestBody : public AMQMethodBody +{ + public: + typedef boost::shared_ptr<AMQRequestBody> shared_ptr; + + struct Data { + Data(RequestId id=0, ResponseId mark=0) + : requestId(id), responseMark(mark) {} + void encode(Buffer&) const; + void decode(Buffer&); + + RequestId requestId; + ResponseId responseMark; + }; + + static Data& getData(const AMQBody::shared_ptr& body) { + return boost::dynamic_pointer_cast<AMQRequestBody>(body)->getData(); + } + + static shared_ptr create( + AMQP_MethodVersionMap& versionMap, ProtocolVersion version, + Buffer& buffer); + + AMQRequestBody(ProtocolVersion v, RequestId id=0, ResponseId mark=0) + : AMQMethodBody(v), data(id, mark) {} + + uint8_t type() const { return REQUEST_BODY; } + void encode(Buffer& buffer) const; + + Data& getData() { return data; } + RequestId getRequestId() const { return data.requestId; } + ResponseId getResponseMark() const { return data.responseMark; } + void setRequestId(RequestId id) { data.requestId=id; } + void setResponseMark(ResponseId mark) { data.responseMark=mark; } + + bool isRequest()const { return true; } + protected: + static const uint32_t baseSize() { return AMQMethodBody::baseSize()+20; } + void printPrefix(std::ostream& out) const; + + private: + Data data; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_AMQRequestBody_h*/ diff --git a/cpp/lib/common/framing/AMQResponseBody.cpp b/cpp/lib/common/framing/AMQResponseBody.cpp new file mode 100644 index 0000000000..7da71a5d25 --- /dev/null +++ b/cpp/lib/common/framing/AMQResponseBody.cpp @@ -0,0 +1,65 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "AMQFrame.h" +#include "AMQResponseBody.h" +#include "AMQP_MethodVersionMap.h" + +namespace qpid { +namespace framing { + +void AMQResponseBody::Data::encode(Buffer& buffer) const { + buffer.putLongLong(responseId); + buffer.putLongLong(requestId); + buffer.putLong(batchOffset); +} + +void AMQResponseBody::Data::decode(Buffer& buffer) { + responseId = buffer.getLongLong(); + requestId = buffer.getLongLong(); + batchOffset = buffer.getLong(); +} + +void AMQResponseBody::encode(Buffer& buffer) const { + data.encode(buffer); + encodeId(buffer); + encodeContent(buffer); +} + +AMQResponseBody::shared_ptr AMQResponseBody::create( + AMQP_MethodVersionMap& versionMap, ProtocolVersion version, + Buffer& buffer) +{ + ClassMethodId id; + Data data; + data.decode(buffer); + id.decode(buffer); + AMQResponseBody* body = dynamic_cast<AMQResponseBody*>( + versionMap.createMethodBody( + id.classId, id.methodId, version.getMajor(), version.getMinor())); + assert(body); + body->data = data; + return AMQResponseBody::shared_ptr(body); +} + +void AMQResponseBody::printPrefix(std::ostream& out) const { + out << "response(id=" << data.responseId << ",request=" << data.requestId + << ",batch=" << data.batchOffset << "): "; +} + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/AMQResponseBody.h b/cpp/lib/common/framing/AMQResponseBody.h new file mode 100644 index 0000000000..fa381baddd --- /dev/null +++ b/cpp/lib/common/framing/AMQResponseBody.h @@ -0,0 +1,85 @@ +#ifndef _framing_AMQResponseBody_h +#define _framing_AMQResponseBody_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "AMQMethodBody.h" + +namespace qpid { +namespace framing { + +class AMQP_MethodVersionMap; + +/** + * Body of a response method frame. + */ +class AMQResponseBody : public AMQMethodBody +{ + + public: + typedef boost::shared_ptr<AMQResponseBody> shared_ptr; + + struct Data { + Data(ResponseId id=0, RequestId req=0, BatchOffset off=0) + : responseId(id), requestId(req), batchOffset(off) {} + void encode(Buffer&) const; + void decode(Buffer&); + + uint64_t responseId; + uint64_t requestId; + uint32_t batchOffset; + }; + + static Data& getData(const AMQBody::shared_ptr& body) { + return boost::dynamic_pointer_cast<AMQResponseBody>(body)->getData(); + } + + static shared_ptr create( + AMQP_MethodVersionMap& versionMap, ProtocolVersion version, + Buffer& buffer); + + AMQResponseBody( + ProtocolVersion v, ResponseId id=0, RequestId req=0, BatchOffset off=0) + : AMQMethodBody(v), data(id, req, off) {} + + uint8_t type() const { return RESPONSE_BODY; } + void encode(Buffer& buffer) const; + + Data& getData() { return data; } + ResponseId getResponseId() const { return data.responseId; } + RequestId getRequestId() const { return data.requestId; } + BatchOffset getBatchOffset() const { return data.batchOffset; } + void setResponseId(ResponseId id) { data.responseId = id; } + void setRequestId(RequestId id) { data.requestId = id; } + void setBatchOffset(BatchOffset id) { data.batchOffset = id; } + + bool isResponse() const { return true; } + protected: + static const uint32_t baseSize() { return AMQMethodBody::baseSize()+20; } + void printPrefix(std::ostream& out) const; + + private: + Data data; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_AMQResponseBody_h*/ diff --git a/cpp/lib/common/framing/BasicHeaderProperties.cpp b/cpp/lib/common/framing/BasicHeaderProperties.cpp new file mode 100644 index 0000000000..930ec9f4dd --- /dev/null +++ b/cpp/lib/common/framing/BasicHeaderProperties.cpp @@ -0,0 +1,103 @@ +/* + * + * 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 <BasicHeaderProperties.h> + +//TODO: This could be easily generated from the spec + +qpid::framing::BasicHeaderProperties::BasicHeaderProperties() : deliveryMode(0), priority(0), timestamp(0){} +qpid::framing::BasicHeaderProperties::~BasicHeaderProperties(){} + +uint32_t qpid::framing::BasicHeaderProperties::size() const{ + uint32_t bytes = 2;//flags + if(contentType.length() > 0) bytes += contentType.length() + 1; + if(contentEncoding.length() > 0) bytes += contentEncoding.length() + 1; + if(headers.count() > 0) bytes += headers.size(); + if(deliveryMode != 0) bytes += 1; + if(priority != 0) bytes += 1; + if(correlationId.length() > 0) bytes += correlationId.length() + 1; + if(replyTo.length() > 0) bytes += replyTo.length() + 1; + if(expiration.length() > 0) bytes += expiration.length() + 1; + if(messageId.length() > 0) bytes += messageId.length() + 1; + if(timestamp != 0) bytes += 8; + if(type.length() > 0) bytes += type.length() + 1; + if(userId.length() > 0) bytes += userId.length() + 1; + if(appId.length() > 0) bytes += appId.length() + 1; + if(clusterId.length() > 0) bytes += clusterId.length() + 1; + + return bytes; +} + +void qpid::framing::BasicHeaderProperties::encode(qpid::framing::Buffer& buffer) const{ + uint16_t flags = getFlags(); + buffer.putShort(flags); + + if(contentType.length() > 0) buffer.putShortString(contentType); + if(contentEncoding.length() > 0) buffer.putShortString(contentEncoding); + if(headers.count() > 0) buffer.putFieldTable(headers); + if(deliveryMode != 0) buffer.putOctet(deliveryMode); + if(priority != 0) buffer.putOctet(priority); + if(correlationId.length() > 0) buffer.putShortString(correlationId); + if(replyTo.length() > 0) buffer.putShortString(replyTo); + if(expiration.length() > 0) buffer.putShortString(expiration); + if(messageId.length() > 0) buffer.putShortString(messageId); + if(timestamp != 0) buffer.putLongLong(timestamp);; + if(type.length() > 0) buffer.putShortString(type); + if(userId.length() > 0) buffer.putShortString(userId); + if(appId.length() > 0) buffer.putShortString(appId); + if(clusterId.length() > 0) buffer.putShortString(clusterId); +} + +void qpid::framing::BasicHeaderProperties::decode(qpid::framing::Buffer& buffer, uint32_t /*size*/){ + uint16_t flags = buffer.getShort(); + if(flags & (1 << 15)) buffer.getShortString(contentType); + if(flags & (1 << 14)) buffer.getShortString(contentEncoding); + if(flags & (1 << 13)) buffer.getFieldTable(headers); + if(flags & (1 << 12)) deliveryMode = buffer.getOctet(); + if(flags & (1 << 11)) priority = buffer.getOctet(); + if(flags & (1 << 10)) buffer.getShortString(correlationId); + if(flags & (1 << 9)) buffer.getShortString(replyTo); + if(flags & (1 << 8)) buffer.getShortString(expiration); + if(flags & (1 << 7)) buffer.getShortString(messageId); + if(flags & (1 << 6)) timestamp = buffer.getLongLong(); + if(flags & (1 << 5)) buffer.getShortString(type); + if(flags & (1 << 4)) buffer.getShortString(userId); + if(flags & (1 << 3)) buffer.getShortString(appId); + if(flags & (1 << 2)) buffer.getShortString(clusterId); +} + +uint16_t qpid::framing::BasicHeaderProperties::getFlags() const{ + uint16_t flags(0); + if(contentType.length() > 0) flags |= (1 << 15); + if(contentEncoding.length() > 0) flags |= (1 << 14); + if(headers.count() > 0) flags |= (1 << 13); + if(deliveryMode != 0) flags |= (1 << 12); + if(priority != 0) flags |= (1 << 11); + if(correlationId.length() > 0) flags |= (1 << 10); + if(replyTo.length() > 0) flags |= (1 << 9); + if(expiration.length() > 0) flags |= (1 << 8); + if(messageId.length() > 0) flags |= (1 << 7); + if(timestamp != 0) flags |= (1 << 6); + if(type.length() > 0) flags |= (1 << 5); + if(userId.length() > 0) flags |= (1 << 4); + if(appId.length() > 0) flags |= (1 << 3); + if(clusterId.length() > 0) flags |= (1 << 2); + return flags; +} diff --git a/cpp/lib/common/framing/BasicHeaderProperties.h b/cpp/lib/common/framing/BasicHeaderProperties.h new file mode 100644 index 0000000000..1f3fd31250 --- /dev/null +++ b/cpp/lib/common/framing/BasicHeaderProperties.h @@ -0,0 +1,97 @@ +/* + * + * 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 <amqp_types.h> +#include <Buffer.h> +#include <FieldTable.h> +#include <HeaderProperties.h> + +#ifndef _BasicHeaderProperties_ +#define _BasicHeaderProperties_ + +namespace qpid { +namespace framing { + enum delivery_mode {TRANSIENT = 1, PERSISTENT = 2}; + + //TODO: This could be easily generated from the spec + class BasicHeaderProperties : public HeaderProperties + { + string contentType; + string contentEncoding; + FieldTable headers; + uint8_t deliveryMode; + uint8_t priority; + string correlationId; + string replyTo; + string expiration; + string messageId; + uint64_t timestamp; + string type; + string userId; + string appId; + string clusterId; + + uint16_t getFlags() const; + + public: + BasicHeaderProperties(); + virtual ~BasicHeaderProperties(); + virtual uint32_t size() const; + virtual void encode(Buffer& buffer) const; + virtual void decode(Buffer& buffer, uint32_t size); + + inline virtual uint8_t classId() { return BASIC; } + + inline const string& getContentType() const { return contentType; } + inline const string& getContentEncoding() const { return contentEncoding; } + inline FieldTable& getHeaders() { return headers; } + inline uint8_t getDeliveryMode() const { return deliveryMode; } + inline uint8_t getPriority() const { return priority; } + inline const string& getCorrelationId() const {return correlationId; } + inline const string& getReplyTo() const { return replyTo; } + inline const string& getExpiration() const { return expiration; } + inline const string& getMessageId() const {return messageId; } + inline uint64_t getTimestamp() const { return timestamp; } + inline const string& getType() const { return type; } + inline const string& getUserId() const { return userId; } + inline const string& getAppId() const { return appId; } + inline const string& getClusterId() const { return clusterId; } + + void inline setContentType(const string& _type){ contentType = _type; } + void inline setContentEncoding(const string& encoding){ contentEncoding = encoding; } + void inline setHeaders(const FieldTable& _headers){ headers = _headers; } + void inline setDeliveryMode(uint8_t mode){ deliveryMode = mode; } + void inline setPriority(uint8_t _priority){ priority = _priority; } + void inline setCorrelationId(const string& _correlationId){ correlationId = _correlationId; } + void inline setReplyTo(const string& _replyTo){ replyTo = _replyTo;} + void inline setExpiration(const string& _expiration){ expiration = _expiration; } + void inline setMessageId(const string& _messageId){ messageId = _messageId; } + void inline setTimestamp(uint64_t _timestamp){ timestamp = _timestamp; } + void inline setType(const string& _type){ type = _type; } + void inline setUserId(const string& _userId){ userId = _userId; } + void inline setAppId(const string& _appId){appId = _appId; } + void inline setClusterId(const string& _clusterId){ clusterId = _clusterId; } + }; + +} +} + + +#endif diff --git a/cpp/lib/common/framing/BodyHandler.cpp b/cpp/lib/common/framing/BodyHandler.cpp new file mode 100644 index 0000000000..5dd0c0c23d --- /dev/null +++ b/cpp/lib/common/framing/BodyHandler.cpp @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "QpidError.h" +#include "BodyHandler.h" +#include <AMQRequestBody.h> +#include <AMQResponseBody.h> +#include <AMQMethodBody.h> +#include <AMQHeaderBody.h> +#include <AMQContentBody.h> +#include <AMQHeartbeatBody.h> + +using namespace qpid::framing; +using namespace boost; + +BodyHandler::~BodyHandler() {} + +void BodyHandler::handleBody(shared_ptr<AMQBody> body) { + switch(body->type()) + { + case REQUEST_BODY: + handleRequest(shared_polymorphic_cast<AMQRequestBody>(body)); + break; + case RESPONSE_BODY: + handleResponse(shared_polymorphic_cast<AMQResponseBody>(body)); + break; + case METHOD_BODY: + handleMethod(shared_polymorphic_cast<AMQMethodBody>(body)); + break; + case HEADER_BODY: + handleHeader(shared_polymorphic_cast<AMQHeaderBody>(body)); + break; + case CONTENT_BODY: + handleContent(shared_polymorphic_cast<AMQContentBody>(body)); + break; + case HEARTBEAT_BODY: + handleHeartbeat(shared_polymorphic_cast<AMQHeartbeatBody>(body)); + break; + default: + QPID_ERROR(PROTOCOL_ERROR, "Unknown frame type "+body->type()); + } +} + diff --git a/cpp/lib/common/framing/BodyHandler.h b/cpp/lib/common/framing/BodyHandler.h new file mode 100644 index 0000000000..cb3f0997b0 --- /dev/null +++ b/cpp/lib/common/framing/BodyHandler.h @@ -0,0 +1,61 @@ +#ifndef _BodyHandler_ +#define _BodyHandler_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> + +#include "Requester.h" +#include "Responder.h" + +namespace qpid { +namespace framing { + +class AMQRequestBody; +class AMQResponseBody; +class AMQMethodBody; +class AMQHeaderBody; +class AMQContentBody; +class AMQHeartbeatBody; + +/** + * Interface to handle incoming frame bodies. + * Derived classes provide logic for each frame type. + */ +class BodyHandler { + public: + virtual ~BodyHandler(); + virtual void handleBody(boost::shared_ptr<AMQBody> body); + + protected: + virtual void handleRequest(boost::shared_ptr<AMQRequestBody>) = 0; + virtual void handleResponse(boost::shared_ptr<AMQResponseBody>) = 0; + virtual void handleMethod(boost::shared_ptr<AMQMethodBody>) = 0; + virtual void handleHeader(boost::shared_ptr<AMQHeaderBody>) = 0; + virtual void handleContent(boost::shared_ptr<AMQContentBody>) = 0; + virtual void handleHeartbeat(boost::shared_ptr<AMQHeartbeatBody>) = 0; +}; + +}} + + +#endif diff --git a/cpp/lib/common/framing/Buffer.cpp b/cpp/lib/common/framing/Buffer.cpp new file mode 100644 index 0000000000..52c9a42d55 --- /dev/null +++ b/cpp/lib/common/framing/Buffer.cpp @@ -0,0 +1,183 @@ +/* + * + * 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 <Buffer.h> +#include <FramingContent.h> +#include <FieldTable.h> + +qpid::framing::Buffer::Buffer(uint32_t _size) : size(_size), owner(true), position(0), limit(_size){ + data = new char[size]; +} + +qpid::framing::Buffer::Buffer(char* _data, uint32_t _size) : size(_size), owner(false), data(_data), position(0), limit(_size){ +} + +qpid::framing::Buffer::~Buffer(){ + if(owner) delete[] data; +} + +void qpid::framing::Buffer::flip(){ + limit = position; + position = 0; +} + +void qpid::framing::Buffer::clear(){ + limit = size; + position = 0; +} + +void qpid::framing::Buffer::compact(){ + uint32_t p = limit - position; + //copy p chars from position to 0 + memmove(data, data + position, p); + limit = size; + position = p; +} + +void qpid::framing::Buffer::record(){ + r_position = position; + r_limit = limit; +} + +void qpid::framing::Buffer::restore(){ + position = r_position; + limit = r_limit; +} + +uint32_t qpid::framing::Buffer::available(){ + return limit - position; +} + +char* qpid::framing::Buffer::start(){ + return data + position; +} + +void qpid::framing::Buffer::move(uint32_t bytes){ + position += bytes; +} + +void qpid::framing::Buffer::putOctet(uint8_t i){ + data[position++] = i; +} + +void qpid::framing::Buffer::putShort(uint16_t i){ + uint16_t b = i; + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); +} + +void qpid::framing::Buffer::putLong(uint32_t i){ + uint32_t b = i; + data[position++] = (uint8_t) (0xFF & (b >> 24)); + data[position++] = (uint8_t) (0xFF & (b >> 16)); + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); +} + +void qpid::framing::Buffer::putLongLong(uint64_t i){ + uint32_t hi = i >> 32; + uint32_t lo = i; + putLong(hi); + putLong(lo); +} + +uint8_t qpid::framing::Buffer::getOctet(){ + return (uint8_t) data[position++]; +} + +uint16_t qpid::framing::Buffer::getShort(){ + uint16_t hi = (unsigned char) data[position++]; + hi = hi << 8; + hi |= (unsigned char) data[position++]; + return hi; +} + +uint32_t qpid::framing::Buffer::getLong(){ + uint32_t a = (unsigned char) data[position++]; + uint32_t b = (unsigned char) data[position++]; + uint32_t c = (unsigned char) data[position++]; + uint32_t d = (unsigned char) data[position++]; + a = a << 24; + a |= b << 16; + a |= c << 8; + a |= d; + return a; +} + +uint64_t qpid::framing::Buffer::getLongLong(){ + uint64_t hi = getLong(); + uint64_t lo = getLong(); + hi = hi << 32; + return hi | lo; +} + + +void qpid::framing::Buffer::putShortString(const string& s){ + uint8_t len = s.length(); + putOctet(len); + s.copy(data + position, len); + position += len; +} + +void qpid::framing::Buffer::putLongString(const string& s){ + uint32_t len = s.length(); + putLong(len); + s.copy(data + position, len); + position += len; +} + +void qpid::framing::Buffer::getShortString(string& s){ + uint8_t len = getOctet(); + s.assign(data + position, len); + position += len; +} + +void qpid::framing::Buffer::getLongString(string& s){ + uint32_t len = getLong(); + s.assign(data + position, len); + position += len; +} + +void qpid::framing::Buffer::putFieldTable(const FieldTable& t){ + t.encode(*this); +} + +void qpid::framing::Buffer::getFieldTable(FieldTable& t){ + t.decode(*this); +} + +void qpid::framing::Buffer::putContent(const Content& c){ + c.encode(*this); +} + +void qpid::framing::Buffer::getContent(Content& c){ + c.decode(*this); +} + +void qpid::framing::Buffer::putRawData(const string& s){ + uint32_t len = s.length(); + s.copy(data + position, len); + position += len; +} + +void qpid::framing::Buffer::getRawData(string& s, uint32_t len){ + s.assign(data + position, len); + position += len; +} diff --git a/cpp/lib/common/framing/Buffer.h b/cpp/lib/common/framing/Buffer.h new file mode 100644 index 0000000000..63a15c7c3d --- /dev/null +++ b/cpp/lib/common/framing/Buffer.h @@ -0,0 +1,86 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <amqp_types.h> + +#ifndef _Buffer_ +#define _Buffer_ + +namespace qpid { +namespace framing { + +class Content; +class FieldTable; + +class Buffer +{ + const uint32_t size; + const bool owner;//indicates whether the data is owned by this instance + char* data; + uint32_t position; + uint32_t limit; + uint32_t r_position; + uint32_t r_limit; + +public: + + Buffer(uint32_t size); + Buffer(char* data, uint32_t size); + ~Buffer(); + + void flip(); + void clear(); + void compact(); + void record(); + void restore(); + uint32_t available(); + char* start(); + void move(uint32_t bytes); + + void putOctet(uint8_t i); + void putShort(uint16_t i); + void putLong(uint32_t i); + void putLongLong(uint64_t i); + + uint8_t getOctet(); + uint16_t getShort(); + uint32_t getLong(); + uint64_t getLongLong(); + + void putShortString(const string& s); + void putLongString(const string& s); + void getShortString(string& s); + void getLongString(string& s); + + void putFieldTable(const FieldTable& t); + void getFieldTable(FieldTable& t); + + void putContent(const Content& c); + void getContent(Content& c); + + void putRawData(const string& s); + void getRawData(string& s, uint32_t size); + +}; + +}} // namespace qpid::framing + + +#endif diff --git a/cpp/lib/common/framing/ChannelAdapter.cpp b/cpp/lib/common/framing/ChannelAdapter.cpp new file mode 100644 index 0000000000..8a1ff39ee5 --- /dev/null +++ b/cpp/lib/common/framing/ChannelAdapter.cpp @@ -0,0 +1,99 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <boost/format.hpp> + +#include "ChannelAdapter.h" +#include "AMQFrame.h" +#include "Exception.h" + +using boost::format; + +namespace qpid { +namespace framing { + +void ChannelAdapter::init( + ChannelId i, OutputHandler& o, ProtocolVersion v) +{ + assertChannelNotOpen(); + id = i; + out = &o; + version = v; +} + +RequestId ChannelAdapter::send(AMQBody::shared_ptr body) { + RequestId result = 0; + assertChannelOpen(); + switch (body->type()) { + case REQUEST_BODY: { + AMQRequestBody::shared_ptr request = + boost::shared_polymorphic_downcast<AMQRequestBody>(body); + requester.sending(request->getData()); + result = request->getData().requestId; + break; + } + case RESPONSE_BODY: { + AMQResponseBody::shared_ptr response = + boost::shared_polymorphic_downcast<AMQResponseBody>(body); + responder.sending(response->getData()); + break; + } + } + out->send(new AMQFrame(getVersion(), getId(), body)); + return result; +} + +void ChannelAdapter::handleRequest(AMQRequestBody::shared_ptr request) { + assertMethodOk(*request); + AMQRequestBody::Data& requestData = request->getData(); + responder.received(requestData); + handleMethodInContext(request, MethodContext(this, request)); +} + +void ChannelAdapter::handleResponse(AMQResponseBody::shared_ptr response) { + assertMethodOk(*response); + // TODO aconway 2007-01-30: Consider a response handled on receipt. + // Review - any cases where this is not the case? + AMQResponseBody::Data& responseData = response->getData(); + requester.processed(responseData); + handleMethod(response); +} + +void ChannelAdapter::handleMethod(AMQMethodBody::shared_ptr method) { + assertMethodOk(*method); + handleMethodInContext(method, MethodContext(this, method)); +} + +void ChannelAdapter::assertMethodOk(AMQMethodBody& method) const { + if (getId() != 0 && method.amqpClassId() == ConnectionOpenBody::CLASS_ID) + throw ConnectionException( + 504, format("Connection method on non-0 channel %d.")%getId()); +} + +void ChannelAdapter::assertChannelOpen() const { + if (getId() != 0 && !isOpen()) + throw ConnectionException( + 504, format("Channel %d is not open.")%getId()); +} + +void ChannelAdapter::assertChannelNotOpen() const { + if (getId() != 0 && isOpen()) + throw ConnectionException( + 504, format("Channel %d is already open.") % getId()); +} + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/ChannelAdapter.h b/cpp/lib/common/framing/ChannelAdapter.h new file mode 100644 index 0000000000..f6e3986eed --- /dev/null +++ b/cpp/lib/common/framing/ChannelAdapter.h @@ -0,0 +1,105 @@ +#ifndef _ChannelAdapter_ +#define _ChannelAdapter_ + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <boost/shared_ptr.hpp> + +#include "BodyHandler.h" +#include "Requester.h" +#include "Responder.h" +#include "framing/amqp_types.h" + +namespace qpid { +namespace framing { + +class MethodContext; + +// FIXME aconway 2007-02-20: Rename as ChannelBase or just Channel. + +/** + * Base class for client and broker channels. + * + * - receives frame bodies from the network. + * - Updates request/response data. + * - Dispatches requests with a MethodContext for responses. + * + * send() + * - Updates request/resposne ID data. + * - Forwards frame to the peer. + * + * Thread safety: OBJECT UNSAFE. Instances must not be called + * concurrently. AMQP defines channels to be serialized. + */ +class ChannelAdapter : public BodyHandler { + public: + /** + *@param output Processed frames are forwarded to this handler. + */ + ChannelAdapter(ChannelId id_=0, OutputHandler* out_=0, + ProtocolVersion ver=ProtocolVersion()) + : id(id_), out(out_), version(ver) {} + + /** Initialize the channel adapter. */ + void init(ChannelId, OutputHandler&, ProtocolVersion); + + ChannelId getId() const { return id; } + ProtocolVersion getVersion() const { return version; } + + /** + * Wrap body in a frame and send the frame. + * Takes ownership of body. + */ + RequestId send(AMQBody::shared_ptr body); + RequestId send(AMQBody* body) { return send(AMQBody::shared_ptr(body)); } + + void handleMethod(boost::shared_ptr<qpid::framing::AMQMethodBody>); + void handleRequest(boost::shared_ptr<qpid::framing::AMQRequestBody>); + void handleResponse(boost::shared_ptr<qpid::framing::AMQResponseBody>); + + virtual bool isOpen() const = 0; + + protected: + void assertMethodOk(AMQMethodBody& method) const; + void assertChannelOpen() const; + void assertChannelNotOpen() const; + + virtual void handleMethodInContext( + boost::shared_ptr<qpid::framing::AMQMethodBody> method, + const MethodContext& context) = 0; + + RequestId getFirstAckRequest() { return requester.getFirstAckRequest(); } + RequestId getLastAckRequest() { return requester.getLastAckRequest(); } + RequestId getNextSendRequestId() { return requester.getNextId(); } + + private: + ChannelId id; + OutputHandler* out; + ProtocolVersion version; + Requester requester; + Responder responder; +}; + +}} + + +#endif diff --git a/cpp/lib/common/framing/FieldTable.cpp b/cpp/lib/common/framing/FieldTable.cpp new file mode 100644 index 0000000000..5bbc4651d3 --- /dev/null +++ b/cpp/lib/common/framing/FieldTable.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 <FieldTable.h> +#include <QpidError.h> +#include <Buffer.h> +#include <Value.h> +#include <assert.h> + +namespace qpid { +namespace framing { + +FieldTable::~FieldTable() {} + +uint32_t FieldTable::size() const { + uint32_t len(4); + for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + // 2 = shortstr_len_byyte + type_char_byte + len += 2 + (i->first).size() + (i->second)->size(); + } + return len; +} + +int FieldTable::count() const { + return values.size(); +} + +namespace +{ +std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_type& i) { + return out << i.first << ":" << *i.second; +} +} + +std::ostream& operator<<(std::ostream& out, const FieldTable& t) { + out << "{"; + FieldTable::ValueMap::const_iterator i = t.getMap().begin(); + if (i != t.getMap().end()) out << *i++; + while (i != t.getMap().end()) + { + out << "," << *i++; + } + return out << "}"; +} + +void FieldTable::setString(const std::string& name, const std::string& value){ + values[name] = ValuePtr(new StringValue(value)); +} + +void FieldTable::setInt(const std::string& name, int value){ + values[name] = ValuePtr(new IntegerValue(value)); +} + +void FieldTable::setTimestamp(const std::string& name, uint64_t value){ + values[name] = ValuePtr(new TimeValue(value)); +} + +void FieldTable::setTable(const std::string& name, const FieldTable& value){ + values[name] = ValuePtr(new FieldTableValue(value)); +} + +namespace { +template <class T> T default_value() { return T(); } +template <> int default_value<int>() { return 0; } +template <> uint64_t default_value<uint64_t>() { return 0; } +} + +template <class T> +T FieldTable::getValue(const std::string& name) const +{ + ValueMap::const_iterator i = values.find(name); + if (i == values.end()) return default_value<T>(); + const ValueOps<T> *vt = dynamic_cast<const ValueOps<T>*>(i->second.get()); + return vt->getValue(); +} + +std::string FieldTable::getString(const std::string& name) const { + return getValue<std::string>(name); +} + +int FieldTable::getInt(const std::string& name) const { + return getValue<int>(name); +} + +uint64_t FieldTable::getTimestamp(const std::string& name) const { + return getValue<uint64_t>(name); +} + +void FieldTable::getTable(const std::string& name, FieldTable& value) const { + value = getValue<FieldTable>(name); +} + +void FieldTable::encode(Buffer& buffer) const{ + buffer.putLong(size() - 4); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + buffer.putShortString(i->first); + buffer.putOctet(i->second->getType()); + i->second->encode(buffer); + } +} + +void FieldTable::decode(Buffer& buffer){ + uint32_t len = buffer.getLong(); + uint32_t available = buffer.available(); + if (available < len) + THROW_QPID_ERROR(FRAMING_ERROR, "Not enough data for field table."); + uint32_t leftover = available - len; + while(buffer.available() > leftover){ + std::string name; + buffer.getShortString(name); + std::auto_ptr<Value> value(Value::decode_value(buffer)); + values[name] = ValuePtr(value.release()); + } +} + + +bool FieldTable::operator==(const FieldTable& x) const { + if (values.size() != x.values.size()) return false; + for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + ValueMap::const_iterator j = x.values.find(i->first); + if (j == x.values.end()) return false; + if (*(i->second) != *(j->second)) return false; + } + return true; +} + +void FieldTable::erase(const std::string& name) +{ + values.erase(values.find(name)); +} + +} +} diff --git a/cpp/lib/common/framing/FieldTable.h b/cpp/lib/common/framing/FieldTable.h new file mode 100644 index 0000000000..e25a7d3f8c --- /dev/null +++ b/cpp/lib/common/framing/FieldTable.h @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <vector> +#include <boost/shared_ptr.hpp> +#include <map> +#include <amqp_types.h> + +#ifndef _FieldTable_ +#define _FieldTable_ + +namespace qpid { + /** + * The framing namespace contains classes that are used to create, + * send and receive the basic packets from which AMQP is built. + */ +namespace framing { + +class Value; +class Buffer; + +/** + * A set of name-value pairs. (See the AMQP spec for more details on + * AMQP field tables). + * + * \ingroup clientapi + */ +class FieldTable +{ + public: + typedef boost::shared_ptr<Value> ValuePtr; + typedef std::map<std::string, ValuePtr> ValueMap; + + ~FieldTable(); + uint32_t size() const; + int count() const; + void setString(const std::string& name, const std::string& value); + void setInt(const std::string& name, int value); + void setTimestamp(const std::string& name, uint64_t value); + void setTable(const std::string& name, const FieldTable& value); + //void setDecimal(string& name, xxx& value); + std::string getString(const std::string& name) const; + int getInt(const std::string& name) const; + uint64_t getTimestamp(const std::string& name) const; + void getTable(const std::string& name, FieldTable& value) const; + //void getDecimal(string& name, xxx& value); + void erase(const std::string& name); + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + + bool operator==(const FieldTable& other) const; + + // TODO aconway 2006-09-26: Yeuch! Rework FieldTable to have + // a map-like interface. + const ValueMap& getMap() const { return values; } + ValueMap& getMap() { return values; } + + private: + friend std::ostream& operator<<(std::ostream& out, const FieldTable& body); + ValueMap values; + template<class T> T getValue(const std::string& name) const; +}; + +class FieldNotFoundException{}; +class UnknownFieldName : public FieldNotFoundException{}; +class IncorrectFieldType : public FieldNotFoundException{}; +} +} + + +#endif diff --git a/cpp/lib/common/framing/FramingContent.cpp b/cpp/lib/common/framing/FramingContent.cpp new file mode 100644 index 0000000000..24efa38dcb --- /dev/null +++ b/cpp/lib/common/framing/FramingContent.cpp @@ -0,0 +1,75 @@ +/* + * + * 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 <assert.h> + +#include "Buffer.h" +#include "FramingContent.h" +#include <QpidError.h> +#include <sstream> + +namespace qpid { +namespace framing { + +Content::Content() : discriminator(0) {} + +Content::Content(uint8_t _discriminator, const string& _value): discriminator(_discriminator), value(_value) { + validate(); +} + +void Content::validate() { + if (discriminator == REFERENCE) { + if(value.empty()) { + THROW_QPID_ERROR(FRAMING_ERROR, "Reference cannot be empty"); + } + }else if (discriminator != INLINE) { + std::stringstream out; + out << "Invalid discriminator: " << (int) discriminator; + THROW_QPID_ERROR(FRAMING_ERROR, out.str()); + } +} + +Content::~Content() {} + +void Content::encode(Buffer& buffer) const { + buffer.putOctet(discriminator); + buffer.putLongString(value); +} + +void Content::decode(Buffer& buffer) { + discriminator = buffer.getOctet(); + buffer.getLongString(value); + validate(); +} + +size_t Content::size() const { + return 1/*discriminator*/ + 4/*for recording size of long string*/ + value.size(); +} + +std::ostream& operator<<(std::ostream& out, const Content& content) { + if (content.discriminator == REFERENCE) { + out << "{REF:" << content.value << "}"; + } else if (content.discriminator == INLINE) { + out << "{INLINE:" << content.value.size() << " bytes}"; + } + return out; +} + +}} // namespace framing::qpid diff --git a/cpp/lib/common/framing/FramingContent.h b/cpp/lib/common/framing/FramingContent.h new file mode 100644 index 0000000000..696bcc7c1a --- /dev/null +++ b/cpp/lib/common/framing/FramingContent.h @@ -0,0 +1,40 @@ +#ifndef _framing_FramingContent_h +#define _framing_FramingContent_h + +#include <ostream> + +namespace qpid { +namespace framing { + +enum discriminator_types { INLINE = 0, REFERENCE = 1 }; + +/** + * A representation of the AMQP 'content' data type (used for message + * bodies) which can hold inline data or a reference. + */ +class Content +{ + uint8_t discriminator; + string value; + + void validate(); + + public: + Content(); + Content(uint8_t _discriminator, const string& _value); + ~Content(); + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + size_t size() const; + bool isInline() const { return discriminator == INLINE; } + bool isReference() const { return discriminator == REFERENCE; } + const string& getValue() const { return value; } + + friend std::ostream& operator<<(std::ostream&, const Content&); +}; + +}} // namespace qpid::framing + + +#endif /*!_framing_FramingContent_h*/ diff --git a/cpp/lib/common/framing/HeaderProperties.h b/cpp/lib/common/framing/HeaderProperties.h new file mode 100644 index 0000000000..1ec4840309 --- /dev/null +++ b/cpp/lib/common/framing/HeaderProperties.h @@ -0,0 +1,46 @@ +/* + * + * 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 <amqp_types.h> +#include <Buffer.h> + +#ifndef _HeaderProperties_ +#define _HeaderProperties_ + +namespace qpid { +namespace framing { + + enum header_classes{BASIC = 60}; + + class HeaderProperties + { + + public: + inline virtual ~HeaderProperties(){} + virtual uint8_t classId() = 0; + virtual uint32_t size() const = 0; + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, uint32_t size) = 0; + }; +} +} + + +#endif diff --git a/cpp/lib/common/framing/InitiationHandler.cpp b/cpp/lib/common/framing/InitiationHandler.cpp new file mode 100644 index 0000000000..dd92c9859b --- /dev/null +++ b/cpp/lib/common/framing/InitiationHandler.cpp @@ -0,0 +1,24 @@ +/* + * + * 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 <InitiationHandler.h> + +qpid::framing::InitiationHandler::~InitiationHandler() {} diff --git a/cpp/lib/common/framing/InitiationHandler.h b/cpp/lib/common/framing/InitiationHandler.h new file mode 100644 index 0000000000..d94fc58d2c --- /dev/null +++ b/cpp/lib/common/framing/InitiationHandler.h @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> + +#ifndef _InitiationHandler_ +#define _InitiationHandler_ + +#include <ProtocolInitiation.h> + +namespace qpid { +namespace framing { + + class InitiationHandler{ + public: + virtual ~InitiationHandler(); + virtual void initiated(ProtocolInitiation* header) = 0; + }; + +} +} + + +#endif diff --git a/cpp/lib/common/framing/InputHandler.h b/cpp/lib/common/framing/InputHandler.h new file mode 100644 index 0000000000..4e2d4bcc9b --- /dev/null +++ b/cpp/lib/common/framing/InputHandler.h @@ -0,0 +1,39 @@ +#ifndef _InputHandler_ +#define _InputHandler_ +/* + * + * 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 <AMQFrame.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace framing { + +class InputHandler : private boost::noncopyable { + public: + virtual ~InputHandler() {} + virtual void received(AMQFrame* frame) = 0; +}; + +}} + + +#endif diff --git a/cpp/lib/common/framing/MethodContext.cpp b/cpp/lib/common/framing/MethodContext.cpp new file mode 100644 index 0000000000..73af73f8e5 --- /dev/null +++ b/cpp/lib/common/framing/MethodContext.cpp @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "MethodContext.h" +#include "amqp_types.h" +#include "AMQRequestBody.h" + +namespace qpid { +namespace framing { + +RequestId MethodContext::getRequestId() const { + return boost::shared_polymorphic_downcast<AMQRequestBody>(methodBody) + ->getRequestId(); +} + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/MethodContext.h b/cpp/lib/common/framing/MethodContext.h new file mode 100644 index 0000000000..3493924bf6 --- /dev/null +++ b/cpp/lib/common/framing/MethodContext.h @@ -0,0 +1,80 @@ +#ifndef _framing_MethodContext_h +#define _framing_MethodContext_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/shared_ptr.hpp> + +#include "OutputHandler.h" +#include "ProtocolVersion.h" + +namespace qpid { +namespace framing { + +class BodyHandler; +class AMQMethodBody; +class ChannelAdapter; + +/** + * Invocation context for an AMQP method. + * + * It provides the method being processed and the channel on which + * it arrived. + * + * All Handler functions take a MethodContext as the last parameter. + */ +struct MethodContext +{ + typedef boost::shared_ptr<AMQMethodBody> BodyPtr; + + MethodContext(ChannelAdapter* ch=0, BodyPtr method=BodyPtr()) + : channel(ch), methodBody(method) {} + + /** + * Channel on which the method being processed arrived. + * 0 if the method was constructed by the caller + * rather than received from a channel. + */ + ChannelAdapter* channel; + + /** + * Body of the method being processed. + * It's useful for passing around instead of unpacking all its parameters. + * It's also provides the request ID when constructing a response. + */ + BodyPtr methodBody; + + /** + * Return methodBody's request ID. + * It is an error to call this if methodBody is not a request. + */ + RequestId getRequestId() const; +}; + +// FIXME aconway 2007-02-01: Method context only required on Handler +// functions, not on Proxy functions. If we add set/getChannel(ChannelAdapter*) +// on AMQBody and set it during decodeing then we could get rid of the context. + + + +}} // namespace qpid::framing + + + +#endif /*!_framing_MethodContext_h*/ diff --git a/cpp/lib/common/framing/OutputHandler.h b/cpp/lib/common/framing/OutputHandler.h new file mode 100644 index 0000000000..9ffd4227d8 --- /dev/null +++ b/cpp/lib/common/framing/OutputHandler.h @@ -0,0 +1,39 @@ +#ifndef _OutputHandler_ +#define _OutputHandler_ + +/* + * + * 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/noncopyable.hpp> + +namespace qpid { +namespace framing { +class AMQFrame; + +class OutputHandler : private boost::noncopyable { + public: + virtual ~OutputHandler() {} + virtual void send(AMQFrame* frame) = 0; +}; + +}} + + +#endif diff --git a/cpp/lib/common/framing/ProtocolInitiation.cpp b/cpp/lib/common/framing/ProtocolInitiation.cpp new file mode 100644 index 0000000000..de53488f7b --- /dev/null +++ b/cpp/lib/common/framing/ProtocolInitiation.cpp @@ -0,0 +1,63 @@ +/* + * + * 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 <ProtocolInitiation.h> + +namespace qpid { +namespace framing { + +ProtocolInitiation::ProtocolInitiation(){} + +ProtocolInitiation::ProtocolInitiation(uint8_t _major, uint8_t _minor) : version(_major, _minor) {} + +ProtocolInitiation::ProtocolInitiation(ProtocolVersion p) : version(p) {} + +ProtocolInitiation::~ProtocolInitiation(){} + +void ProtocolInitiation::encode(Buffer& buffer){ + buffer.putOctet('A'); + buffer.putOctet('M'); + buffer.putOctet('Q'); + buffer.putOctet('P'); + buffer.putOctet(1);//class + buffer.putOctet(1);//instance + buffer.putOctet(version.getMajor()); + buffer.putOctet(version.getMinor()); +} + +bool ProtocolInitiation::decode(Buffer& buffer){ + if(buffer.available() >= 8){ + buffer.getOctet();//A + buffer.getOctet();//M + buffer.getOctet();//Q + buffer.getOctet();//P + buffer.getOctet();//class + buffer.getOctet();//instance + version.setMajor(buffer.getOctet()); + version.setMinor(buffer.getOctet()); + return true; + }else{ + return false; + } +} + +//TODO: this should prbably be generated from the spec at some point to keep the version numbers up to date + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/ProtocolInitiation.h b/cpp/lib/common/framing/ProtocolInitiation.h new file mode 100644 index 0000000000..ed7b59e94e --- /dev/null +++ b/cpp/lib/common/framing/ProtocolInitiation.h @@ -0,0 +1,54 @@ +/* + * + * 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 <amqp_types.h> +#include <Buffer.h> +#include <AMQDataBlock.h> +#include <ProtocolVersion.h> + +#ifndef _ProtocolInitiation_ +#define _ProtocolInitiation_ + +namespace qpid { +namespace framing { + +class ProtocolInitiation : public AMQDataBlock +{ +private: + ProtocolVersion version; + +public: + ProtocolInitiation(); + ProtocolInitiation(uint8_t major, uint8_t minor); + ProtocolInitiation(ProtocolVersion p); + virtual ~ProtocolInitiation(); + virtual void encode(Buffer& buffer); + virtual bool decode(Buffer& buffer); + inline virtual uint32_t size() const { return 8; } + inline uint8_t getMajor() const { return version.getMajor(); } + inline uint8_t getMinor() const { return version.getMinor(); } + inline ProtocolVersion getVersion() const { return version; } +}; + +} +} + + +#endif diff --git a/cpp/lib/common/framing/ProtocolVersion.cpp b/cpp/lib/common/framing/ProtocolVersion.cpp new file mode 100644 index 0000000000..fd4b1a645f --- /dev/null +++ b/cpp/lib/common/framing/ProtocolVersion.cpp @@ -0,0 +1,44 @@ +/* + * + * 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 <ProtocolVersion.h> +#include <sstream> + +using namespace qpid::framing; + +const std::string ProtocolVersion::toString() const +{ + std::stringstream ss; + ss << major_ << "-" << minor_; + return ss.str(); +} + +ProtocolVersion& ProtocolVersion::operator=(ProtocolVersion p) +{ + major_ = p.major_; + minor_ = p.minor_; + return *this; +} + +bool ProtocolVersion::operator==(ProtocolVersion p) const +{ + return major_ == p.major_ && minor_ == p.minor_; +} + diff --git a/cpp/lib/common/framing/ProtocolVersion.h b/cpp/lib/common/framing/ProtocolVersion.h new file mode 100644 index 0000000000..5e1429c1ea --- /dev/null +++ b/cpp/lib/common/framing/ProtocolVersion.h @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ProtocolVersion_ +#define _ProtocolVersion_ + +#include <amqp_types.h> + +namespace qpid +{ +namespace framing +{ + +class ProtocolVersion +{ +private: + uint8_t major_; + uint8_t minor_; + +public: + ProtocolVersion(uint8_t _major=0, uint8_t _minor=0) + : major_(_major), minor_(_minor) {} + + uint8_t getMajor() const { return major_; } + void setMajor(uint8_t major) { major_ = major; } + uint8_t getMinor() const { return minor_; } + void setMinor(uint8_t minor) { minor_ = minor; } + const std::string toString() const; + + ProtocolVersion& operator=(ProtocolVersion p); + + bool operator==(ProtocolVersion p) const; + bool operator!=(ProtocolVersion p) const { return ! (*this == p); } +}; + +} // namespace framing +} // namespace qpid + + +#endif // ifndef _ProtocolVersion_ diff --git a/cpp/lib/common/framing/ProtocolVersionException.cpp b/cpp/lib/common/framing/ProtocolVersionException.cpp new file mode 100644 index 0000000000..9088422f6f --- /dev/null +++ b/cpp/lib/common/framing/ProtocolVersionException.cpp @@ -0,0 +1,33 @@ +/* + * + * 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/format.hpp> +#include <ProtocolVersionException.h> + + +using namespace qpid::framing; + +void ProtocolVersionException::init(const std::string& msg) +{ + whatStr = boost::str( + boost::format("ProtocolVersionException: %s found: %s") + % versionFound.toString() % msg); +} + diff --git a/cpp/lib/common/framing/ProtocolVersionException.h b/cpp/lib/common/framing/ProtocolVersionException.h new file mode 100644 index 0000000000..8e2de8b843 --- /dev/null +++ b/cpp/lib/common/framing/ProtocolVersionException.h @@ -0,0 +1,56 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _ProtocolVersionException_ +#define _ProtocolVersionException_ + +#include <Exception.h> +#include <ProtocolVersion.h> +#include <string> +#include <vector> + +namespace qpid { +namespace framing { + +class ProtocolVersionException : public qpid::Exception +{ +protected: + ProtocolVersion versionFound; + +public: + ~ProtocolVersionException() throw() {} + + template <class T> + ProtocolVersionException( + ProtocolVersion ver, const T& msg) throw () : versionFound(ver) + { init(boost::lexical_cast<std::string>(msg)); } + + template <class T> + ProtocolVersionException(const T& msg) throw () + { init(boost::lexical_cast<std::string>(msg)); } + + private: + void init(const std::string& msg); +}; + +}} // namespace qpid::framing + +#endif //ifndef _ProtocolVersionException_ diff --git a/cpp/lib/common/framing/Proxy.cpp b/cpp/lib/common/framing/Proxy.cpp new file mode 100644 index 0000000000..0b2a882a49 --- /dev/null +++ b/cpp/lib/common/framing/Proxy.cpp @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Proxy.h" +#include "ChannelAdapter.h" +#include "ProtocolVersion.h" + +namespace qpid { +namespace framing { + +Proxy::~Proxy() {} + +ProtocolVersion Proxy::getProtocolVersion() const { + return channel.getVersion(); +} + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/Proxy.h b/cpp/lib/common/framing/Proxy.h new file mode 100644 index 0000000000..8ed46ed748 --- /dev/null +++ b/cpp/lib/common/framing/Proxy.h @@ -0,0 +1,51 @@ +#ifndef _framing_Proxy_h +#define _framing_Proxy_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "ProtocolVersion.h" + +namespace qpid { +namespace framing { + +class ChannelAdapter; +class FieldTable; +class Content; + +/** + * Base class for proxies. + */ +class Proxy +{ + + public: + Proxy(ChannelAdapter& ch) : channel(ch) {} + virtual ~Proxy(); + + ProtocolVersion getProtocolVersion() const; + + protected: + ChannelAdapter& channel; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_Proxy_h*/ diff --git a/cpp/lib/common/framing/Requester.cpp b/cpp/lib/common/framing/Requester.cpp new file mode 100644 index 0000000000..9ee809e2ee --- /dev/null +++ b/cpp/lib/common/framing/Requester.cpp @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/format.hpp> + +#include "Requester.h" +#include "QpidError.h" + +namespace qpid { +namespace framing { + +Requester::Requester() : lastId(0), responseMark(0) {} + +void Requester::sending(AMQRequestBody::Data& request) { + request.requestId = ++lastId; + request.responseMark = responseMark; +} + +void Requester::processed(const AMQResponseBody::Data& response) { + responseMark = response.responseId; + firstAckRequest = response.requestId; + lastAckRequest = firstAckRequest + response.batchOffset; +} + +}} // namespace qpid::framing diff --git a/cpp/lib/common/framing/Requester.h b/cpp/lib/common/framing/Requester.h new file mode 100644 index 0000000000..dcc4460041 --- /dev/null +++ b/cpp/lib/common/framing/Requester.h @@ -0,0 +1,67 @@ +#ifndef _framing_Requester_h +#define _framing_Requester_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <set> +#include "AMQRequestBody.h" +#include "AMQResponseBody.h" + +namespace qpid { +namespace framing { + +class AMQRequestBody; +class AMQResponseBody; + +/** + * Manage request IDs and the response mark for locally initiated requests. + * + * THREAD UNSAFE: This class is called as frames are sent or received + * sequentially on a connection, so it does not need to be thread safe. + */ +class Requester +{ + public: + Requester(); + + /** Called before sending a request to set request data. */ + void sending(AMQRequestBody::Data&); + + /** Called after processing a response. */ + void processed(const AMQResponseBody::Data&); + + /** Get the next request id to be used. */ + RequestId getNextId() { return lastId + 1; } + /** Get the first request acked by this response */ + RequestId getFirstAckRequest() { return firstAckRequest; } + /** Get the last request acked by this response */ + RequestId getLastAckRequest() { return lastAckRequest; } + + private: + RequestId lastId; + ResponseId responseMark; + ResponseId firstAckRequest; + ResponseId lastAckRequest; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_Requester_h*/ diff --git a/cpp/lib/common/framing/Responder.cpp b/cpp/lib/common/framing/Responder.cpp new file mode 100644 index 0000000000..c8c5ce8dcc --- /dev/null +++ b/cpp/lib/common/framing/Responder.cpp @@ -0,0 +1,43 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <boost/format.hpp> + +#include "Responder.h" +#include "QpidError.h" + +namespace qpid { +namespace framing { + +Responder::Responder() : lastId(0), responseMark(0) {} + +void Responder::received(const AMQRequestBody::Data& request) { + if (request.responseMark < responseMark || request.responseMark > lastId) + THROW_QPID_ERROR( + PROTOCOL_ERROR, boost::format("Invalid response mark %d.") + %request.responseMark); + responseMark = request.responseMark; +} + +void Responder::sending(AMQResponseBody::Data& response) { + response.responseId = ++lastId; + assert(response.requestId); // Should be already set. + response.batchOffset = 0; +} + +}} // namespace qpid::framing + diff --git a/cpp/lib/common/framing/Responder.h b/cpp/lib/common/framing/Responder.h new file mode 100644 index 0000000000..0e1785256b --- /dev/null +++ b/cpp/lib/common/framing/Responder.h @@ -0,0 +1,61 @@ +#ifndef _framing_Responder_h +#define _framing_Responder_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "AMQRequestBody.h" +#include "AMQResponseBody.h" + +namespace qpid { +namespace framing { + +/** + * Manage response ids and response mark remotely initianted requests. + * + * THREAD UNSAFE: This class is called as frames are sent or received + * sequentially on a connection, so it does not need to be thread safe. + */ +class Responder +{ + public: + Responder(); + + /** Called after receiving a request. */ + void received(const AMQRequestBody::Data& request); + + /** Called before sending a response to set respose data. */ + void sending(AMQResponseBody::Data& response); + + /** Get the ID of the highest response acknowledged by the peer. */ + ResponseId getResponseMark() { return responseMark; } + + // TODO aconway 2007-01-14: Batching support - store unsent + // Response for equality comparison with subsequent responses. + // + + private: + ResponseId lastId; + ResponseId responseMark; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_Responder_h*/ diff --git a/cpp/lib/common/framing/Value.cpp b/cpp/lib/common/framing/Value.cpp new file mode 100644 index 0000000000..03e005e384 --- /dev/null +++ b/cpp/lib/common/framing/Value.cpp @@ -0,0 +1,122 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <Value.h> +#include <Buffer.h> +#include <FieldTable.h> +#include <QpidError.h> +#include <sstream> + +namespace qpid { +namespace framing { + +Value::~Value() {} + +void StringValue::encode(Buffer& buffer){ + buffer.putLongString(value); +} +void StringValue::decode(Buffer& buffer){ + buffer.getLongString(value); +} + +void IntegerValue::encode(Buffer& buffer){ + buffer.putLong((uint32_t) value); +} +void IntegerValue::decode(Buffer& buffer){ + value = buffer.getLong(); +} + +void TimeValue::encode(Buffer& buffer){ + buffer.putLongLong(value); +} +void TimeValue::decode(Buffer& buffer){ + value = buffer.getLongLong(); +} + +void DecimalValue::encode(Buffer& buffer){ + buffer.putOctet(value.decimals); + buffer.putLong(value.value); +} +void DecimalValue::decode(Buffer& buffer){ + value = Decimal(buffer.getLong(), buffer.getOctet()); +} + +void FieldTableValue::encode(Buffer& buffer){ + buffer.putFieldTable(value); +} +void FieldTableValue::decode(Buffer& buffer){ + buffer.getFieldTable(value); +} + +std::auto_ptr<Value> Value::decode_value(Buffer& buffer) +{ + std::auto_ptr<Value> value; + uint8_t type = buffer.getOctet(); + switch(type){ + case 'S': + value.reset(new StringValue()); + break; + case 'I': + value.reset(new IntegerValue()); + break; + case 'D': + value.reset(new DecimalValue()); + break; + case 'T': + value.reset(new TimeValue()); + break; + case 'F': + value.reset(new FieldTableValue()); + break; + + //non-standard types, introduced in java client for JMS compliance + case 'x': + value.reset(new BinaryValue()); + break; + default: + std::stringstream out; + out << "Unknown field table value type: " << type; + THROW_QPID_ERROR(FRAMING_ERROR, out.str()); + } + value->decode(buffer); + return value; +} + +EmptyValue::~EmptyValue() {} + +void EmptyValue::print(std::ostream& out) const +{ + out << "<empty field value>"; +} + +std::ostream& operator<<(std::ostream& out, const Value& v) { + v.print(out); + return out; +} + +std::ostream& operator<<(std::ostream& out, const Decimal& d) +{ + return out << "Decimal(" << d.value << "," << d.decimals << ")"; +} + +}} + + + diff --git a/cpp/lib/common/framing/Value.h b/cpp/lib/common/framing/Value.h new file mode 100644 index 0000000000..8752b02f40 --- /dev/null +++ b/cpp/lib/common/framing/Value.h @@ -0,0 +1,171 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <vector> +#include <amqp_types.h> +#include <FieldTable.h> + +#ifndef _Value_ +#define _Value_ + +namespace qpid { +namespace framing { + +class Buffer; + +/** + * Represents a decimal value. + * No arithmetic functionality for now, we only care about encoding/decoding. + */ +struct Decimal { + uint32_t value; + uint8_t decimals; + + Decimal(uint32_t value_=0, uint8_t decimals_=0) : value(value_), decimals(decimals_) {} + bool operator==(const Decimal& d) const { + return decimals == d.decimals && value == d.value; + } + bool operator!=(const Decimal& d) const { return !(*this == d); } +}; + +std::ostream& operator<<(std::ostream& out, const Decimal& d); + +/** + * Polymorpic base class for values. + */ +class Value { + public: + virtual ~Value(); + virtual uint32_t size() const = 0; + virtual char getType() const = 0; + virtual void encode(Buffer& buffer) = 0; + virtual void decode(Buffer& buffer) = 0; + virtual bool operator==(const Value&) const = 0; + bool operator!=(const Value& v) const { return !(*this == v); } + virtual void print(std::ostream& out) const = 0; + + /** Create a new value by decoding from the buffer */ + static std::auto_ptr<Value> decode_value(Buffer& buffer); +}; + +std::ostream& operator<<(std::ostream& out, const Value& d); + + +/** + * Template for common operations on Value sub-classes. + */ +template <class T> +class ValueOps : public Value +{ + protected: + T value; + public: + ValueOps() {} + ValueOps(const T& v) : value(v) {} + const T& getValue() const { return value; } + T& getValue() { return value; } + + virtual bool operator==(const Value& v) const { + const ValueOps<T>* vo = dynamic_cast<const ValueOps<T>*>(&v); + if (vo == 0) return false; + else return value == vo->value; + } + + void print(std::ostream& out) const { out << value; } +}; + + +class StringValue : public ValueOps<std::string> { + public: + StringValue(const std::string& v) : ValueOps<std::string>(v) {} + StringValue() {} + virtual uint32_t size() const { return 4 + value.length(); } + virtual char getType() const { return 'S'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); +}; + +class IntegerValue : public ValueOps<int> { + public: + IntegerValue(int v) : ValueOps<int>(v) {} + IntegerValue(){} + virtual uint32_t size() const { return 4; } + virtual char getType() const { return 'I'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); +}; + +class TimeValue : public ValueOps<uint64_t> { + public: + TimeValue(uint64_t v) : ValueOps<uint64_t>(v){} + TimeValue(){} + virtual uint32_t size() const { return 8; } + virtual char getType() const { return 'T'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); +}; + +class DecimalValue : public ValueOps<Decimal> { + public: + DecimalValue(const Decimal& d) : ValueOps<Decimal>(d) {} + DecimalValue(uint32_t value_=0, uint8_t decimals_=0) : + ValueOps<Decimal>(Decimal(value_, decimals_)){} + virtual uint32_t size() const { return 5; } + virtual char getType() const { return 'D'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); +}; + + +class FieldTableValue : public ValueOps<FieldTable> { + public: + FieldTableValue(const FieldTable& v) : ValueOps<FieldTable>(v){} + FieldTableValue(){} + virtual uint32_t size() const { return 4 + value.size(); } + virtual char getType() const { return 'F'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); +}; + +class EmptyValue : public Value { + public: + ~EmptyValue(); + virtual uint32_t size() const { return 0; } + virtual char getType() const { return 0; } + virtual void encode(Buffer& ) {} + virtual void decode(Buffer& ) {} + virtual bool operator==(const Value& v) const { + return dynamic_cast<const EmptyValue*>(&v); + } + virtual void print(std::ostream& out) const; +}; + +//non-standard types, introduced in java client for JMS compliance +class BinaryValue : public StringValue { + public: + BinaryValue(const std::string& v) : StringValue(v) {} + BinaryValue() {} + virtual char getType() const { return 'x'; } +}; + +}} // qpid::framing + +#endif diff --git a/cpp/lib/common/framing/amqp_framing.h b/cpp/lib/common/framing/amqp_framing.h new file mode 100644 index 0000000000..62f87352f8 --- /dev/null +++ b/cpp/lib/common/framing/amqp_framing.h @@ -0,0 +1,36 @@ +/* + * + * 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 <amqp_types.h> +#include <AMQFrame.h> +#include <AMQBody.h> +#include <BodyHandler.h> +#include <AMQMethodBody.h> +#include <AMQHeaderBody.h> +#include <AMQContentBody.h> +#include <AMQHeartbeatBody.h> +#include <AMQP_MethodVersionMap.h> +#include <InputHandler.h> +#include <OutputHandler.h> +#include <InitiationHandler.h> +#include <ProtocolInitiation.h> +#include <BasicHeaderProperties.h> +#include <ProtocolVersion.h> +#include <ProtocolVersionException.h> diff --git a/cpp/lib/common/framing/amqp_types.h b/cpp/lib/common/framing/amqp_types.h new file mode 100644 index 0000000000..49963bd570 --- /dev/null +++ b/cpp/lib/common/framing/amqp_types.h @@ -0,0 +1,57 @@ +#ifndef AMQP_TYPES_H +#define AMQP_TYPES_H +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** \file + * Type definitions and forward declarations of all types used to + * in AMQP messages. + */ + +#include <string> +#ifdef _WINDOWS +#include "windows.h" +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +#endif +#ifndef _WINDOWS +#include "stdint.h" +#endif + +namespace qpid { +namespace framing { + +using std::string; +typedef uint16_t ChannelId; +typedef uint64_t RequestId; +typedef uint64_t ResponseId; +typedef uint32_t BatchOffset; +typedef uint16_t ClassId; +typedef uint16_t MethodId; +typedef uint16_t ReplyCode; + +// Types represented by classes. +class Content; +class FieldTable; +}} // namespace qpid::framing +#endif diff --git a/cpp/lib/common/framing/amqp_types_full.h b/cpp/lib/common/framing/amqp_types_full.h new file mode 100644 index 0000000000..6a24a99d38 --- /dev/null +++ b/cpp/lib/common/framing/amqp_types_full.h @@ -0,0 +1,36 @@ +#ifndef _framing_amqp_types_decl_h +#define _framing_amqp_types_decl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** \file + * Type definitions and full declarations of all types used to + * in AMQP messages. + * + * Its better to include amqp_types.h in another header instead of this file + * unless the header actually needs the full declarations. Including + * full declarations when forward declarations would do increases compile + * times. + */ + +#include "amqp_types.h" +#include "FramingContent.h" +#include "FieldTable.h" + +#endif /*!_framing_amqp_types_decl_h*/ diff --git a/cpp/lib/common/sys/Acceptor.h b/cpp/lib/common/sys/Acceptor.h new file mode 100644 index 0000000000..f571dcbddd --- /dev/null +++ b/cpp/lib/common/sys/Acceptor.h @@ -0,0 +1,47 @@ +#ifndef _sys_Acceptor_h +#define _sys_Acceptor_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 <stdint.h> +#include <SharedObject.h> + +namespace qpid { +namespace sys { + +class ConnectionInputHandlerFactory; + +class Acceptor : public qpid::SharedObject<Acceptor> +{ + public: + static Acceptor::shared_ptr create(int16_t port, int backlog, int threads, bool trace = false); + virtual ~Acceptor() = 0; + virtual int16_t getPort() const = 0; + virtual void run(qpid::sys::ConnectionInputHandlerFactory* factory) = 0; + virtual void shutdown() = 0; +}; + +}} + + + +#endif /*!_sys_Acceptor_h*/ diff --git a/cpp/lib/common/sys/AtomicCount.h b/cpp/lib/common/sys/AtomicCount.h new file mode 100644 index 0000000000..63670cbf00 --- /dev/null +++ b/cpp/lib/common/sys/AtomicCount.h @@ -0,0 +1,53 @@ +#ifndef _posix_AtomicCount_h +#define _posix_AtomicCount_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/detail/atomic_count.hpp> +#include "ScopedIncrement.h" + +namespace qpid { +namespace sys { + +/** + * Atomic counter. + */ +class AtomicCount : boost::noncopyable { + public: + typedef ScopedDecrement<AtomicCount> ScopedDecrement; + typedef ScopedIncrement<AtomicCount> ScopedIncrement; + + AtomicCount(long value = 0) : count(value) {} + + void operator++() { ++count ; } + + long operator--() { return --count; } + + operator long() const { return count; } + + + private: + boost::detail::atomic_count count; +}; + + +}} + + +#endif // _posix_AtomicCount_h diff --git a/cpp/lib/common/sys/Condition.h b/cpp/lib/common/sys/Condition.h new file mode 100644 index 0000000000..9d70af5b84 --- /dev/null +++ b/cpp/lib/common/sys/Condition.h @@ -0,0 +1,128 @@ +#ifndef _sys_Condition_h +#define _sys_Condition_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <sys/errno.h> +#include <boost/noncopyable.hpp> +#include <sys/Mutex.h> +#include <sys/Time.h> + +#ifdef USE_APR +# include <apr_thread_cond.h> +#endif + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const Time& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: +#ifdef USE_APR + apr_thread_cond_t* condition; +#else + pthread_cond_t condition; +#endif +}; + + +// APR ================================================================ +#ifdef USE_APR + +Condition::Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get())); +} + +Condition::~Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); +} + +void Condition::wait(Mutex& mutex) { + CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const Time& absoluteTime){ + // APR uses microseconds. + apr_status_t status = + apr_thread_cond_timedwait( + condition, mutex.mutex, absoluteTime/TIME_USEC); + if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status); + return status == 0; +} + +void Condition::notify(){ + CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); +} + +void Condition::notifyAll(){ + CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); +} + +#else +// POSIX ================================================================ + +Condition::Condition() { + QPID_POSIX_THROW_IF(pthread_cond_init(&condition, 0)); +} + +Condition::~Condition() { + QPID_POSIX_THROW_IF(pthread_cond_destroy(&condition)); +} + +void Condition::wait(Mutex& mutex) { + QPID_POSIX_THROW_IF(pthread_cond_wait(&condition, &mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const Time& absoluteTime){ + struct timespec ts; + toTimespec(ts, absoluteTime); + int status = pthread_cond_timedwait(&condition, &mutex.mutex, &ts); + if (status != 0) { + if (status == ETIMEDOUT) return false; + throw QPID_POSIX_ERROR(status); + } + return true; +} + +void Condition::notify(){ + QPID_POSIX_THROW_IF(pthread_cond_signal(&condition)); +} + +void Condition::notifyAll(){ + QPID_POSIX_THROW_IF(pthread_cond_broadcast(&condition)); +} +#endif /*USE_APR*/ + + +}} +#endif /*!_sys_Condition_h*/ diff --git a/cpp/lib/common/sys/ConnectionInputHandler.h b/cpp/lib/common/sys/ConnectionInputHandler.h new file mode 100644 index 0000000000..fa70dfaf48 --- /dev/null +++ b/cpp/lib/common/sys/ConnectionInputHandler.h @@ -0,0 +1,45 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionInputHandler_ +#define _ConnectionInputHandler_ + +#include <InputHandler.h> +#include <InitiationHandler.h> +#include <ProtocolInitiation.h> +#include <sys/TimeoutHandler.h> + +namespace qpid { +namespace sys { + + class ConnectionInputHandler : + public qpid::framing::InitiationHandler, + public qpid::framing::InputHandler, + public TimeoutHandler + { + public: + virtual void closed() = 0; + }; + +} +} + + +#endif diff --git a/cpp/lib/common/sys/ConnectionInputHandlerFactory.h b/cpp/lib/common/sys/ConnectionInputHandlerFactory.h new file mode 100644 index 0000000000..af7d411928 --- /dev/null +++ b/cpp/lib/common/sys/ConnectionInputHandlerFactory.h @@ -0,0 +1,46 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionInputHandlerFactory_ +#define _ConnectionInputHandlerFactory_ + +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class ConnectionOutputHandler; +class ConnectionInputHandler; + +/** + * Callback interface used by the Acceptor to + * create a ConnectionInputHandler for each new connection. + */ +class ConnectionInputHandlerFactory : private boost::noncopyable +{ + public: + virtual ConnectionInputHandler* create(ConnectionOutputHandler* ctxt) = 0; + virtual ~ConnectionInputHandlerFactory(){} +}; + +}} + + +#endif diff --git a/cpp/lib/common/sys/ConnectionOutputHandler.h b/cpp/lib/common/sys/ConnectionOutputHandler.h new file mode 100644 index 0000000000..91849e1dfb --- /dev/null +++ b/cpp/lib/common/sys/ConnectionOutputHandler.h @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ConnectionOutputHandler_ +#define _ConnectionOutputHandler_ + +#include <OutputHandler.h> + +namespace qpid { +namespace sys { + +/** + * Provides the output handler associated with a connection. + */ +class ConnectionOutputHandler : public virtual qpid::framing::OutputHandler +{ + public: + virtual void close() = 0; +}; + +}} + + +#endif diff --git a/cpp/lib/common/sys/Module.h b/cpp/lib/common/sys/Module.h new file mode 100644 index 0000000000..9bf5d6e1fc --- /dev/null +++ b/cpp/lib/common/sys/Module.h @@ -0,0 +1,161 @@ +#ifndef _sys_Module_h +#define _sys_Module_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/noncopyable.hpp> +#include <iostream> +#include <QpidError.h> + +namespace qpid { +namespace sys { +#if USE_APR +#include <apr_dso.h> + typedef apr_dso_handle_t* dso_handle_t; +#else + typedef void* dso_handle_t; +#endif + + template <class T> class Module : private boost::noncopyable + { + typedef T* create_t(); + typedef void destroy_t(T*); + + dso_handle_t handle; + destroy_t* destroy; + T* ptr; + + void load(const std::string& name); + void unload(); + void* getSymbol(const std::string& name); + + public: + Module(const std::string& name); + T* operator->(); + T* get(); + ~Module() throw(); + }; + +} +} + +using namespace qpid::sys; + +template <class T> Module<T>::Module(const std::string& module) : destroy(0), ptr(0) +{ + load(module); + //TODO: need a better strategy for symbol names to allow multiple + //modules to be loaded without clashes... + + //Note: need the double cast to avoid errors in casting from void* to function pointer with -pedantic + create_t* create = reinterpret_cast<create_t*>(reinterpret_cast<intptr_t>(getSymbol("create"))); + destroy = reinterpret_cast<destroy_t*>(reinterpret_cast<intptr_t>(getSymbol("destroy"))); + ptr = create(); +} + +template <class T> T* Module<T>::operator->() +{ + return ptr; +} + +template <class T> T* Module<T>::get() +{ + return ptr; +} + +template <class T> Module<T>::~Module() throw() +{ + try { + if (handle && ptr) { + destroy(ptr); + } + if (handle) unload(); + } catch (std::exception& e) { + std::cout << "Error while destroying module: " << e.what() << std::endl; + } + destroy = 0; + handle = 0; + ptr = 0; +} + +// APR ================================================================ +#if USE_APR + +#include <apr/APRBase.h> +#include <apr/APRPool.h> + +template <class T> void Module<T>::load(const std::string& name) +{ + CHECK_APR_SUCCESS(apr_dso_load(&handle, name.c_str(), APRPool::get())); +} + +template <class T> void Module<T>::unload() +{ + CHECK_APR_SUCCESS(apr_dso_unload(handle)); +} + +template <class T> void* Module<T>::getSymbol(const std::string& name) +{ + apr_dso_handle_sym_t symbol; + CHECK_APR_SUCCESS(apr_dso_sym(&symbol, handle, name.c_str())); + return (void*) symbol; +} + +// POSIX================================================================ +#else + +#include <dlfcn.h> + +template <class T> void Module<T>::load(const std::string& name) +{ + dlerror(); + handle = dlopen(name.c_str(), RTLD_NOW); + const char* error = dlerror(); + if (error) { + THROW_QPID_ERROR(INTERNAL_ERROR, error); + } +} + +template <class T> void Module<T>::unload() +{ + dlerror(); + dlclose(handle); + const char* error = dlerror(); + if (error) { + THROW_QPID_ERROR(INTERNAL_ERROR, error); + } +} + +template <class T> void* Module<T>::getSymbol(const std::string& name) +{ + dlerror(); + void* sym = dlsym(handle, name.c_str()); + const char* error = dlerror(); + if (error) { + THROW_QPID_ERROR(INTERNAL_ERROR, error); + } + return sym; +} + +#endif //if USE_APR + +#endif //ifndef _sys_Module_h + diff --git a/cpp/lib/common/sys/Monitor.h b/cpp/lib/common/sys/Monitor.h new file mode 100644 index 0000000000..a3bbd3c5aa --- /dev/null +++ b/cpp/lib/common/sys/Monitor.h @@ -0,0 +1,55 @@ +#ifndef _sys_Monitor_h +#define _sys_Monitor_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 <sys/errno.h> +#include <sys/Condition.h> + +#ifdef USE_APR +# include <apr_thread_cond.h> +#endif + +namespace qpid { +namespace sys { + +/** + * A monitor is a condition variable and a mutex + */ +class Monitor : public Mutex, public Condition { + public: + using Condition::wait; + inline void wait(); + inline bool wait(const Time& absoluteTime); +}; + + +void Monitor::wait() { + Condition::wait(*this); +} + +bool Monitor::wait(const Time& absoluteTime) { + return Condition::wait(*this, absoluteTime); +} + +}} +#endif /*!_sys_Monitor_h*/ diff --git a/cpp/lib/common/sys/Mutex.h b/cpp/lib/common/sys/Mutex.h new file mode 100644 index 0000000000..9db9be0981 --- /dev/null +++ b/cpp/lib/common/sys/Mutex.h @@ -0,0 +1,165 @@ +#ifndef _sys_Mutex_h +#define _sys_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef USE_APR +# include <apr_thread_mutex.h> +# include <apr/APRBase.h> +# include <apr/APRPool.h> +#else +# include <pthread.h> +# include <posix/check.h> +#endif +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Scoped lock template: calls lock() in ctor, unlock() in dtor. + * L can be any class with lock() and unlock() functions. + */ +template <class L> +class ScopedLock +{ + public: + ScopedLock(L& l) : mutex(l) { l.lock(); } + ~ScopedLock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedUnlock +{ + public: + ScopedUnlock(L& l) : mutex(l) { l.unlock(); } + ~ScopedUnlock() { mutex.lock(); } + private: + L& mutex; +}; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + public: + typedef ScopedLock<Mutex> ScopedLock; + typedef ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline void trylock(); + + protected: +#ifdef USE_APR + apr_thread_mutex_t* mutex; +#else + pthread_mutex_t mutex; +#endif + friend class Condition; +}; + +#ifdef USE_APR +// APR ================================================================ + +Mutex::Mutex() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +Mutex::~Mutex(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void Mutex::lock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} +void Mutex::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +void Mutex::trylock() { + CHECK_APR_SUCCESS(apr_thread_mutex_trylock(mutex)); +} + +#else +// POSIX ================================================================ + +/** + * PODMutex is a POD, can be static-initialized with + * PODMutex m = QPID_PODMUTEX_INITIALIZER + */ +struct PODMutex +{ + typedef ScopedLock<PODMutex> ScopedLock; + + inline void lock(); + inline void unlock(); + inline void trylock(); + + // Must be public to be a POD: + pthread_mutex_t mutex; +}; + +#define QPID_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + + +void PODMutex::lock() { + QPID_POSIX_THROW_IF(pthread_mutex_lock(&mutex)); +} +void PODMutex::unlock() { + QPID_POSIX_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +void PODMutex::trylock() { + QPID_POSIX_THROW_IF(pthread_mutex_trylock(&mutex)); +} + + +Mutex::Mutex() { + QPID_POSIX_THROW_IF(pthread_mutex_init(&mutex, 0)); +} + +Mutex::~Mutex(){ + QPID_POSIX_THROW_IF(pthread_mutex_destroy(&mutex)); +} + +void Mutex::lock() { + QPID_POSIX_THROW_IF(pthread_mutex_lock(&mutex)); +} +void Mutex::unlock() { + QPID_POSIX_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +void Mutex::trylock() { + QPID_POSIX_THROW_IF(pthread_mutex_trylock(&mutex)); +} + +#endif // USE_APR + +}} + + + +#endif /*!_sys_Mutex_h*/ diff --git a/cpp/lib/common/sys/ProducerConsumer.cpp b/cpp/lib/common/sys/ProducerConsumer.cpp new file mode 100644 index 0000000000..3f6156f230 --- /dev/null +++ b/cpp/lib/common/sys/ProducerConsumer.cpp @@ -0,0 +1,141 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include "QpidError.h" +#include "ScopedIncrement.h" +#include "ProducerConsumer.h" + +namespace qpid { +namespace sys { + +// // ================ ProducerConsumer + +ProducerConsumer::ProducerConsumer(size_t init_items) + : items(init_items), waiters(0), stopped(false) +{} + +void ProducerConsumer::stop() { + Mutex::ScopedLock l(monitor); + stopped = true; + monitor.notifyAll(); + // Wait for waiting consumers to wake up. + while (waiters > 0) + monitor.wait(); +} + +size_t ProducerConsumer::available() const { + Mutex::ScopedLock l(monitor); + return items; +} + +size_t ProducerConsumer::consumers() const { + Mutex::ScopedLock l(monitor); + return waiters; +} + +// ================ Lock + +ProducerConsumer::Lock::Lock(ProducerConsumer& p) + : pc(p), lock(p.monitor), status(INCOMPLETE) {} + +bool ProducerConsumer::Lock::isOk() const { + return !pc.isStopped() && status==INCOMPLETE; +} + +void ProducerConsumer::Lock::checkOk() const { + assert(!pc.isStopped()); + assert(status == INCOMPLETE); +} + +ProducerConsumer::Lock::~Lock() { + assert(status != INCOMPLETE || pc.isStopped()); +} + +void ProducerConsumer::Lock::confirm() { + checkOk(); + status = CONFIRMED; +} + +void ProducerConsumer::Lock::cancel() { + checkOk(); + status = CANCELLED; +} + +// ================ ProducerLock + +ProducerConsumer::ProducerLock::ProducerLock(ProducerConsumer& p) : Lock(p) +{} + + +ProducerConsumer::ProducerLock::~ProducerLock() { + if (status == CONFIRMED) { + pc.items++; + pc.monitor.notify(); // Notify a consumer. + } +} + +// ================ ConsumerLock + +ProducerConsumer::ConsumerLock::ConsumerLock(ProducerConsumer& p) : Lock(p) +{ + if (isOk()) { + ScopedIncrement<size_t> inc(pc.waiters); + while (pc.items == 0 && !pc.stopped) { + pc.monitor.wait(); + } + } +} + +ProducerConsumer::ConsumerLock::ConsumerLock( + ProducerConsumer& p, const Time& timeout) : Lock(p) +{ + if (isOk()) { + // Don't wait if timeout==0 + if (timeout == 0) { + if (pc.items == 0) + status = TIMEOUT; + return; + } + else { + Time deadline = now() + timeout; + ScopedIncrement<size_t> inc(pc.waiters); + while (pc.items == 0 && !pc.stopped) { + if (!pc.monitor.wait(deadline)) { + status = TIMEOUT; + return; + } + } + } + } +} + +ProducerConsumer::ConsumerLock::~ConsumerLock() { + if (pc.isStopped()) { + if (pc.waiters == 0) + pc.monitor.notifyAll(); // All waiters woken, notify stop thread(s) + } + else if (status==CONFIRMED) { + pc.items--; + if (pc.items > 0) + pc.monitor.notify(); // Notify another consumer. + } +} + + +}} // namespace qpid::sys diff --git a/cpp/lib/common/sys/ProducerConsumer.h b/cpp/lib/common/sys/ProducerConsumer.h new file mode 100644 index 0000000000..742639323b --- /dev/null +++ b/cpp/lib/common/sys/ProducerConsumer.h @@ -0,0 +1,165 @@ +#ifndef _sys_ProducerConsumer_h +#define _sys_ProducerConsumer_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <boost/noncopyable.hpp> +#include "Exception.h" +#include "sys/Monitor.h" + +namespace qpid { +namespace sys { + +/** + * Producer-consumer synchronisation. + * + * Producers increase the number of available items, consumers reduce it. + * Consumers wait till an item is available. Waiting threads can be + * woken for shutdown using stop(). + * + * Note: Currently implements unbounded producer-consumer, i.e. no limit + * to available items, producers never block. Can be extended to support + * bounded PC if required. + * + // TODO aconway 2007-02-13: example, from tests. +*/ +class ProducerConsumer +{ + public: + ProducerConsumer(size_t init_items=0); + + ~ProducerConsumer() { stop(); } + + /** + * Wake any threads waiting for ProducerLock or ConsumerLock. + *@post No threads are waiting in Producer or Consumer locks. + */ + void stop(); + + /** True if queue is stopped */ + bool isStopped() { return stopped; } + + /** Number of items available for consumers */ + size_t available() const; + + /** Number of consumers waiting for items */ + size_t consumers() const; + + /** True if available == 0 */ + bool empty() const { return available() == 0; } + + /** + * Base class for producer and consumer locks. + */ + class Lock : private boost::noncopyable { + public: + + /** + * You must call isOk() after creating a lock to verify its state. + * + *@return true means the lock succeeded. You MUST call either + *confirm() or cancel() before the lock goes out of scope. + * + * false means the lock failed - timed out or the + * ProducerConsumer is stopped. You should not do anything in + * the scope of the lock. + */ + bool isOk() const; + + /** + * Confirm that an item was produced/consumed. + *@pre isOk() + */ + void confirm(); + + /** + * Cancel the lock to indicate nothing was produced/consumed. + * Note that locks are not actually released until destroyed. + * + *@pre isOk() + */ + void cancel(); + + /** True if this lock experienced a timeout */ + bool isTimedOut() const { return status == TIMEOUT; } + + /** True if we have been stopped */ + bool isStopped() const { return pc.isStopped(); } + + ProducerConsumer& pc; + + protected: + /** Lock status */ + enum Status { INCOMPLETE, CONFIRMED, CANCELLED, TIMEOUT }; + + Lock(ProducerConsumer& p); + ~Lock(); + void checkOk() const; + Mutex::ScopedLock lock; + Status status; + }; + + /** Lock for code that produces items. */ + struct ProducerLock : public Lock { + /** + * Acquire locks to produce an item. + *@post If isOk() the calling thread has exclusive access + * to produce an item. + */ + ProducerLock(ProducerConsumer& p); + + /** Release locks, signal waiting consumers if confirm() was called. */ + ~ProducerLock(); + }; + + /** Lock for code that consumes items */ + struct ConsumerLock : public Lock { + /** + * Wait for an item to consume and acquire locks. + * + *@post If isOk() there is at least one item available and the + *calling thread has exclusive access to consume it. + */ + ConsumerLock(ProducerConsumer& p); + + /** + * Wait up to timeout to acquire lock. + *@post If isOk() caller has a producer lock. + * If isTimedOut() there was a timeout. + * If neither then we were stopped. + */ + ConsumerLock(ProducerConsumer& p, const Time& timeout); + + /** Release locks */ + ~ConsumerLock(); + }; + + private: + mutable Monitor monitor; + size_t items; + size_t waiters; + bool stopped; + + friend class Lock; + friend class ProducerLock; + friend class ConsumerLock; +}; + +}} // namespace qpid::sys + +#endif /*!_sys_ProducerConsumer_h*/ diff --git a/cpp/lib/common/sys/Runnable.cpp b/cpp/lib/common/sys/Runnable.cpp new file mode 100644 index 0000000000..30122c682f --- /dev/null +++ b/cpp/lib/common/sys/Runnable.cpp @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "Runnable.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace sys { + +Runnable::~Runnable() {} + +Runnable::Functor Runnable::functor() +{ + return boost::bind(&Runnable::run, this); +} + +}} diff --git a/cpp/lib/common/sys/Runnable.h b/cpp/lib/common/sys/Runnable.h new file mode 100644 index 0000000000..fb3927c612 --- /dev/null +++ b/cpp/lib/common/sys/Runnable.h @@ -0,0 +1,50 @@ +#ifndef _Runnable_ +#define _Runnable_ +/* + * + * 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/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Interface for objects that can be run, e.g. in a thread. + */ +class Runnable +{ + public: + /** Type to represent a runnable as a Functor */ + typedef boost::function0<void> Functor; + + virtual ~Runnable(); + + /** Derived classes override run(). */ + virtual void run() = 0; + + /** Create a functor object that will call this->run(). */ + Functor functor(); +}; + +}} + + +#endif diff --git a/cpp/lib/common/sys/ScopedIncrement.h b/cpp/lib/common/sys/ScopedIncrement.h new file mode 100644 index 0000000000..f14461ddaf --- /dev/null +++ b/cpp/lib/common/sys/ScopedIncrement.h @@ -0,0 +1,59 @@ +#ifndef _posix_ScopedIncrement_h +#define _posix_ScopedIncrement_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +/** Increment counter in constructor and decrement in destructor. */ +template <class T> +class ScopedIncrement : boost::noncopyable +{ + public: + ScopedIncrement(T& c) : count(c) { ++count; } + ~ScopedIncrement() { --count; } + private: + T& count; +}; + + +/** Decrement counter in constructor and increment in destructor. */ +template <class T> +class ScopedDecrement : boost::noncopyable +{ + public: + ScopedDecrement(T& c) : count(c) { value = --count; } + ~ScopedDecrement() { ++count; } + + /** Return the value after the decrement. */ + operator long() { return value; } + + private: + T& count; + long value; +}; + + +}} + + +#endif // _posix_ScopedIncrement_h diff --git a/cpp/lib/common/sys/ShutdownHandler.h b/cpp/lib/common/sys/ShutdownHandler.h new file mode 100644 index 0000000000..88baecb5b6 --- /dev/null +++ b/cpp/lib/common/sys/ShutdownHandler.h @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _ShutdownHandler_ +#define _ShutdownHandler_ + +namespace qpid { +namespace sys { + + class ShutdownHandler + { + public: + virtual void shutdown() = 0; + virtual ~ShutdownHandler(){} + }; + +} +} + +#endif diff --git a/cpp/lib/common/sys/Socket.h b/cpp/lib/common/sys/Socket.h new file mode 100644 index 0000000000..d793a240c6 --- /dev/null +++ b/cpp/lib/common/sys/Socket.h @@ -0,0 +1,88 @@ +#ifndef _sys_Socket_h +#define _sys_Socket_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 <string> +#include <sys/Time.h> + +#ifdef USE_APR +# include <apr_network_io.h> +#endif + +namespace qpid { +namespace sys { + +class Socket +{ + public: + /** Create an initialized TCP socket */ + static Socket createTcp(); + + /** Create a socket wrapper for descriptor. */ +#ifdef USE_APR + Socket(apr_socket_t* descriptor = 0); +#else + Socket(int descriptor = 0); +#endif + + /** Set timeout for read and write */ + void setTimeout(Time interval); + + void connect(const std::string& host, int port); + + void close(); + + enum { SOCKET_TIMEOUT=-2, SOCKET_EOF=-3 } ErrorCode; + + /** Returns bytes sent or an ErrorCode value < 0. */ + ssize_t send(const void* data, size_t size); + + /** + * Returns bytes received, an ErrorCode value < 0 or 0 + * if the connection closed in an orderly manner. + */ + ssize_t recv(void* data, size_t size); + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@return The bound port. + */ + int listen(int port = 0, int backlog = 10); + + /** Get file descriptor */ + int fd(); + + private: +#ifdef USE_APR + apr_socket_t* socket; +#else + void init() const; + mutable int socket; // Initialized on demand. +#endif +}; + +}} + + +#endif /*!_sys_Socket_h*/ diff --git a/cpp/lib/common/sys/Thread.h b/cpp/lib/common/sys/Thread.h new file mode 100644 index 0000000000..47b95b6234 --- /dev/null +++ b/cpp/lib/common/sys/Thread.h @@ -0,0 +1,142 @@ +#ifndef _sys_Thread_h +#define _sys_Thread_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <sys/Runnable.h> + +#ifdef USE_APR +# include <apr_thread_proc.h> +# include <apr_portable.h> +# include <apr/APRPool.h> +# include <apr/APRBase.h> +#else +# include <posix/check.h> +# include <pthread.h> +#endif + +namespace qpid { +namespace sys { + +class Thread +{ + public: + inline static Thread current(); + inline static void yield(); + + inline Thread(); + inline explicit Thread(qpid::sys::Runnable*); + inline explicit Thread(qpid::sys::Runnable&); + + inline void join(); + + inline long id(); + + private: +#ifdef USE_APR + static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data); + inline Thread(apr_thread_t* t); + apr_thread_t* thread; +#else + static void* runRunnable(void* runnable); + inline Thread(pthread_t); + pthread_t thread; +#endif +}; + + +Thread::Thread() : thread(0) {} + +// APR ================================================================ +#ifdef USE_APR + +Thread::Thread(Runnable* runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get())); +} + +Thread::Thread(Runnable& runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get())); +} + +void Thread::join(){ + apr_status_t status; + if (thread != 0) + CHECK_APR_SUCCESS(apr_thread_join(&status, thread)); +} + +long Thread::id() { + return long(thread); +} + +Thread::Thread(apr_thread_t* t) : thread(t) {} + +Thread Thread::current(){ + apr_thread_t* thr; + apr_os_thread_t osthr = apr_os_thread_current(); + CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get())); + return Thread(thr); +} + +void Thread::yield() +{ + apr_thread_yield(); +} + + +// POSIX ================================================================ +#else + +Thread::Thread(Runnable* runnable) { + QPID_POSIX_THROW_IF(pthread_create(&thread, NULL, runRunnable, runnable)); +} + +Thread::Thread(Runnable& runnable) { + QPID_POSIX_THROW_IF(pthread_create(&thread, NULL, runRunnable, &runnable)); +} + +void Thread::join(){ + QPID_POSIX_THROW_IF(pthread_join(thread, 0)); +} + +long Thread::id() { + return long(thread); +} + +Thread::Thread(pthread_t thr) : thread(thr) {} + +Thread Thread::current() { + return Thread(pthread_self()); +} + +void Thread::yield() +{ + QPID_POSIX_THROW_IF(pthread_yield()); +} + + +#endif + +}} + +#endif /*!_sys_Thread_h*/ diff --git a/cpp/lib/common/sys/ThreadSafeQueue.h b/cpp/lib/common/sys/ThreadSafeQueue.h new file mode 100644 index 0000000000..ff949a3e16 --- /dev/null +++ b/cpp/lib/common/sys/ThreadSafeQueue.h @@ -0,0 +1,98 @@ +#ifndef _sys_ThreadSafeQueue_h +#define _sys_ThreadSafeQueue_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <deque> +#include "ProducerConsumer.h" +#include "Exception.h" + +namespace qpid { +namespace sys { + +/** + * A thread safe queue template. + */ +template <class T, class ContainerType=std::deque<T> > +class ThreadSafeQueue +{ + public: + + ThreadSafeQueue() {} + + /** Push a value onto the back of the queue */ + void push(const T& value) { + ProducerConsumer::ProducerLock producer(pc); + if (producer.isOk()) { + producer.confirm(); + container.push_back(value); + } + } + + /** Pop a value from the front of the queue. Waits till value is available. + *@throw ShutdownException if queue is stopped while waiting. + */ + T pop() { + ProducerConsumer::ConsumerLock consumer(pc); + if (consumer.isOk()) { + consumer.confirm(); + T value(container.front()); + container.pop_front(); + return value; + } + throw ShutdownException(); + } + + /** + * If a value becomes available within the timeout, set outValue + * and return true. Otherwise return false; + */ + bool pop(T& outValue, const Time& timeout) { + ProducerConsumer::ConsumerLock consumer(pc, timeout); + if (consumer.isOk()) { + consumer.confirm(); + outValue = container.front(); + container.pop_front(); + return true; + } + return false; + } + + /** Interrupt threads waiting in pop() */ + void stop() { pc.stop(); } + + /** True if queue is stopped */ + bool isStopped() { return pc.isStopped(); } + + /** Size of the queue */ + size_t size() { ProducerConsumer::Lock l(pc); return container.size(); } + + /** True if queue is empty */ + bool empty() { ProducerConsumer::Lock l(pc); return container.empty(); } + + private: + ProducerConsumer pc; + ContainerType container; +}; + +}} // namespace qpid::sys + + + +#endif /*!_sys_ThreadSafeQueue_h*/ diff --git a/cpp/lib/common/sys/Time.cpp b/cpp/lib/common/sys/Time.cpp new file mode 100644 index 0000000000..ad6185b966 --- /dev/null +++ b/cpp/lib/common/sys/Time.cpp @@ -0,0 +1,60 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "Time.h" + +namespace qpid { +namespace sys { + +// APR ================================================================ +#if USE_APR + +Time now() { return apr_time_now() * TIME_USEC; } + +// POSIX================================================================ +#else + +Time now() { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return toTime(ts); +} + +struct timespec toTimespec(const Time& t) { + struct timespec ts; + toTimespec(ts, t); + return ts; +} + +struct timespec& toTimespec(struct timespec& ts, const Time& t) { + ts.tv_sec = t / TIME_SEC; + ts.tv_nsec = t % TIME_SEC; + return ts; +} + +Time toTime(const struct timespec& ts) { + return ts.tv_sec*TIME_SEC + ts.tv_nsec; +} + + +#endif +}} + diff --git a/cpp/lib/common/sys/Time.h b/cpp/lib/common/sys/Time.h new file mode 100644 index 0000000000..3dd46741d8 --- /dev/null +++ b/cpp/lib/common/sys/Time.h @@ -0,0 +1,58 @@ +#ifndef _sys_Time_h +#define _sys_Time_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 <stdint.h> + +#ifdef USE_APR +# include <apr_time.h> +#else +# include <time.h> +#endif + +namespace qpid { +namespace sys { + +/** Time in nanoseconds */ +typedef int64_t Time; + +Time now(); + +/** Nanoseconds per second. */ +const Time TIME_SEC = 1000*1000*1000; +/** Nanoseconds per millisecond */ +const Time TIME_MSEC = 1000*1000; +/** Nanoseconds per microseconds. */ +const Time TIME_USEC = 1000; +/** Nanoseconds per nanosecond. */ +const Time TIME_NSEC = 1; + +#ifndef USE_APR +struct timespec toTimespec(const Time& t); +struct timespec& toTimespec(struct timespec& ts, const Time& t); +Time toTime(const struct timespec& ts); +#endif + +}} + +#endif /*!_sys_Time_h*/ diff --git a/cpp/lib/common/sys/TimeoutHandler.h b/cpp/lib/common/sys/TimeoutHandler.h new file mode 100644 index 0000000000..0c10709bbf --- /dev/null +++ b/cpp/lib/common/sys/TimeoutHandler.h @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _TimeoutHandler_ +#define _TimeoutHandler_ + +namespace qpid { +namespace sys { + + class TimeoutHandler + { + public: + virtual void idleOut() = 0; + virtual void idleIn() = 0; + virtual ~TimeoutHandler(){} + }; + +} +} + + +#endif diff --git a/cpp/lib/common/sys/apr/APRAcceptor.cpp b/cpp/lib/common/sys/apr/APRAcceptor.cpp new file mode 100644 index 0000000000..52384857ed --- /dev/null +++ b/cpp/lib/common/sys/apr/APRAcceptor.cpp @@ -0,0 +1,122 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <sys/Acceptor.h> +#include <sys/ConnectionInputHandlerFactory.h> +#include "LFProcessor.h" +#include "LFSessionContext.h" +#include "APRBase.h" +#include "APRPool.h" + +namespace qpid { +namespace sys { + +class APRAcceptor : public Acceptor +{ + public: + APRAcceptor(int16_t port, int backlog, int threads, bool trace); + virtual int16_t getPort() const; + virtual void run(qpid::sys::ConnectionInputHandlerFactory* factory); + virtual void shutdown(); + + private: + void shutdownImpl(); + + private: + int16_t port; + bool trace; + LFProcessor processor; + apr_socket_t* socket; + volatile bool running; + Mutex shutdownLock; +}; + +// Define generic Acceptor::create() to return APRAcceptor. +Acceptor::shared_ptr Acceptor::create(int16_t port, int backlog, int threads, bool trace) +{ + return Acceptor::shared_ptr(new APRAcceptor(port, backlog, threads, trace)); +} +// Must define Acceptor virtual dtor. +Acceptor::~Acceptor() {} + +APRAcceptor::APRAcceptor(int16_t port_, int backlog, int threads, bool trace_) : + port(port_), + trace(trace_), + processor(APRPool::get(), threads, 1000, 5000000), + running(false) +{ + apr_sockaddr_t* address; + CHECK_APR_SUCCESS(apr_sockaddr_info_get(&address, APR_ANYADDR, APR_UNSPEC, port, APR_IPV4_ADDR_OK, APRPool::get())); + CHECK_APR_SUCCESS(apr_socket_create(&socket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, APRPool::get())); + CHECK_APR_SUCCESS(apr_socket_opt_set(socket, APR_SO_REUSEADDR, 1)); + CHECK_APR_SUCCESS(apr_socket_bind(socket, address)); + CHECK_APR_SUCCESS(apr_socket_listen(socket, backlog)); +} + +int16_t APRAcceptor::getPort() const { + apr_sockaddr_t* address; + CHECK_APR_SUCCESS(apr_socket_addr_get(&address, APR_LOCAL, socket)); + return address->port; +} + +void APRAcceptor::run(ConnectionInputHandlerFactory* factory) { + running = true; + processor.start(); + std::cout << "Listening on port " << getPort() << "..." << std::endl; + while(running){ + apr_socket_t* client; + apr_status_t status = apr_socket_accept(&client, socket, APRPool::get()); + if(status == APR_SUCCESS){ + //make this socket non-blocking: + CHECK_APR_SUCCESS(apr_socket_timeout_set(client, 0)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_NONBLOCK, 1)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_TCP_NODELAY, 1)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_SNDBUF, 32768)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_RCVBUF, 32768)); + LFSessionContext* session = new LFSessionContext(APRPool::get(), client, &processor, trace); + session->init(factory->create(session)); + }else{ + Mutex::ScopedLock locker(shutdownLock); + if(running) { + if(status != APR_EINTR){ + std::cout << "ERROR: " << get_desc(status) << std::endl; + } + shutdownImpl(); + } + } + } +} + +void APRAcceptor::shutdown() { + Mutex::ScopedLock locker(shutdownLock); + if (running) { + shutdownImpl(); + } +} + +void APRAcceptor::shutdownImpl() { + Mutex::ScopedLock locker(shutdownLock); + running = false; + processor.stop(); + CHECK_APR_SUCCESS(apr_socket_close(socket)); +} + + +}} diff --git a/cpp/lib/common/sys/apr/APRBase.cpp b/cpp/lib/common/sys/apr/APRBase.cpp new file mode 100644 index 0000000000..861071499f --- /dev/null +++ b/cpp/lib/common/sys/apr/APRBase.cpp @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include <QpidError.h> +#include "APRBase.h" + +using namespace qpid::sys; + +APRBase* APRBase::instance = 0; + +APRBase* APRBase::getInstance(){ + if(instance == 0){ + instance = new APRBase(); + } + return instance; +} + + +APRBase::APRBase() : count(0){ + apr_initialize(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, 0)); + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); +} + +APRBase::~APRBase(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); + apr_pool_destroy(pool); + apr_terminate(); +} + +bool APRBase::_increment(){ + bool deleted(false); + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(this == instance){ + count++; + }else{ + deleted = true; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + return !deleted; +} + +void APRBase::_decrement(){ + APRBase* copy = 0; + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(--count == 0){ + copy = instance; + instance = 0; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + if(copy != 0){ + delete copy; + } +} + +void APRBase::increment(){ + int count = 0; + while(count++ < 2 && !getInstance()->_increment()){ + std::cout << "WARNING: APR initialization triggered concurrently with termination." << std::endl; + } +} + +void APRBase::decrement(){ + getInstance()->_decrement(); +} + +std::string qpid::sys::get_desc(apr_status_t status){ + const int size = 50; + char tmp[size]; + return std::string(apr_strerror(status, tmp, size)); +} + diff --git a/cpp/lib/common/sys/apr/APRBase.h b/cpp/lib/common/sys/apr/APRBase.h new file mode 100644 index 0000000000..6a866a554a --- /dev/null +++ b/cpp/lib/common/sys/apr/APRBase.h @@ -0,0 +1,78 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _APRBase_ +#define _APRBase_ + +#include <string> +#include <apr_thread_mutex.h> +#include <apr_errno.h> +#include <QpidError.h> + +namespace qpid { +namespace sys { + + /** + * Use of APR libraries necessitates explicit init and terminate + * calls. Any class using APR libs should obtain the reference to + * this singleton and increment on construction, decrement on + * destruction. This class can then correctly initialise apr + * before the first use and terminate after the last use. + */ + class APRBase{ + static APRBase* instance; + apr_pool_t* pool; + apr_thread_mutex_t* mutex; + int count; + + APRBase(); + ~APRBase(); + static APRBase* getInstance(); + bool _increment(); + void _decrement(); + public: + static void increment(); + static void decrement(); + }; + + //this is also a convenient place for a helper function for error checking: + void check(apr_status_t status, const char* file, const int line); + std::string get_desc(apr_status_t status); + +#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__); + +} +} + +// Inlined as it is called *a lot* +void inline qpid::sys::check(apr_status_t status, const char* file, const int line){ + if (status != APR_SUCCESS){ + const int size = 50; + char tmp[size]; + std::string msg(apr_strerror(status, tmp, size)); + throw qpid::QpidError(APR_ERROR + ((int) status), msg, + qpid::SrcLine(file, line)); + } +} + + + + +#endif diff --git a/cpp/lib/common/sys/apr/APRPool.cpp b/cpp/lib/common/sys/apr/APRPool.cpp new file mode 100644 index 0000000000..e8b71f6e8a --- /dev/null +++ b/cpp/lib/common/sys/apr/APRPool.cpp @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "APRPool.h" +#include "APRBase.h" +#include <boost/pool/detail/singleton.hpp> + +using namespace qpid::sys; + +APRPool::APRPool(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); +} + +APRPool::~APRPool(){ + apr_pool_destroy(pool); + APRBase::decrement(); +} + +apr_pool_t* APRPool::get() { + return boost::details::pool::singleton_default<APRPool>::instance().pool; +} + diff --git a/cpp/lib/common/sys/apr/APRPool.h b/cpp/lib/common/sys/apr/APRPool.h new file mode 100644 index 0000000000..da7661fcfa --- /dev/null +++ b/cpp/lib/common/sys/apr/APRPool.h @@ -0,0 +1,50 @@ +#ifndef _APRPool_ +#define _APRPool_ + +/* + * + * 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/noncopyable.hpp> +#include <apr_pools.h> + +namespace qpid { +namespace sys { +/** + * Singleton APR memory pool. + */ +class APRPool : private boost::noncopyable { + public: + APRPool(); + ~APRPool(); + + /** Get singleton instance */ + static apr_pool_t* get(); + + private: + apr_pool_t* pool; +}; + +}} + + + + + +#endif /*!_APRPool_*/ diff --git a/cpp/lib/common/sys/apr/APRSocket.cpp b/cpp/lib/common/sys/apr/APRSocket.cpp new file mode 100644 index 0000000000..96dbd132a1 --- /dev/null +++ b/cpp/lib/common/sys/apr/APRSocket.cpp @@ -0,0 +1,78 @@ +/* + * + * 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 "APRBase.h" +#include "APRSocket.h" +#include <assert.h> +#include <iostream> + +using namespace qpid::sys; +using namespace qpid::framing; + +APRSocket::APRSocket(apr_socket_t* _socket) : socket(_socket), closed(false){ + +} + +void APRSocket::read(qpid::framing::Buffer& buffer){ + apr_size_t bytes; + bytes = buffer.available(); + apr_status_t s = apr_socket_recv(socket, buffer.start(), &bytes); + buffer.move(bytes); + if(APR_STATUS_IS_TIMEUP(s)){ + //timed out + }else if(APR_STATUS_IS_EOF(s)){ + close(); + } +} + +void APRSocket::write(qpid::framing::Buffer& buffer){ + apr_size_t bytes; + do{ + bytes = buffer.available(); + apr_socket_send(socket, buffer.start(), &bytes); + buffer.move(bytes); + }while(bytes > 0); +} + +void APRSocket::close(){ + if(!closed){ + std::cout << "Closing socket " << socket << "@" << this << std::endl; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + closed = true; + } +} + +bool APRSocket::isOpen() const { + return !closed; +} + +uint8_t APRSocket::read(){ + char data[1]; + apr_size_t bytes = 1; + apr_status_t s = apr_socket_recv(socket, data, &bytes); + if(APR_STATUS_IS_EOF(s) || bytes == 0){ + return 0; + }else{ + return *data; + } +} + +APRSocket::~APRSocket(){ +} diff --git a/cpp/lib/common/sys/apr/APRSocket.h b/cpp/lib/common/sys/apr/APRSocket.h new file mode 100644 index 0000000000..a55dfc06b0 --- /dev/null +++ b/cpp/lib/common/sys/apr/APRSocket.h @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _APRSocket_ +#define _APRSocket_ + +#include <apr_network_io.h> +#include <Buffer.h> + +namespace qpid { +namespace sys { + + class APRSocket + { + apr_socket_t* const socket; + volatile bool closed; + public: + APRSocket(apr_socket_t* socket); + void read(qpid::framing::Buffer& b); + void write(qpid::framing::Buffer& b); + void close(); + bool isOpen() const; + uint8_t read(); + ~APRSocket(); + }; + +} +} + + +#endif diff --git a/cpp/lib/common/sys/apr/LFProcessor.cpp b/cpp/lib/common/sys/apr/LFProcessor.cpp new file mode 100644 index 0000000000..2b6fc92623 --- /dev/null +++ b/cpp/lib/common/sys/apr/LFProcessor.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 <sstream> +#include <QpidError.h> +#include "LFProcessor.h" +#include "APRBase.h" +#include "LFSessionContext.h" + +using namespace qpid::sys; +using qpid::QpidError; + +// TODO aconway 2006-10-12: stopped is read outside locks. +// + +LFProcessor::LFProcessor(apr_pool_t* pool, int _workers, int _size, int _timeout) : + size(_size), + timeout(_timeout), + signalledCount(0), + current(0), + count(0), + workerCount(_workers), + hasLeader(false), + workers(new Thread[_workers]), + stopped(false) +{ + + CHECK_APR_SUCCESS(apr_pollset_create(&pollset, size, pool, APR_POLLSET_THREADSAFE)); +} + + +LFProcessor::~LFProcessor(){ + if (!stopped) stop(); + delete[] workers; + CHECK_APR_SUCCESS(apr_pollset_destroy(pollset)); +} + +void LFProcessor::start(){ + for(int i = 0; i < workerCount; i++){ + workers[i] = Thread(this); + } +} + +void LFProcessor::add(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); + Monitor::ScopedLock l(countLock); + sessions.push_back(reinterpret_cast<LFSessionContext*>(fd->client_data)); + count++; +} + +void LFProcessor::remove(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); + Monitor::ScopedLock l(countLock); + sessions.erase(find(sessions.begin(), sessions.end(), reinterpret_cast<LFSessionContext*>(fd->client_data))); + count--; +} + +void LFProcessor::reactivate(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); +} + +void LFProcessor::deactivate(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); +} + +void LFProcessor::update(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); +} + +bool LFProcessor::full(){ + Mutex::ScopedLock locker(countLock); + return count == size; +} + +bool LFProcessor::empty(){ + Mutex::ScopedLock locker(countLock); + return count == 0; +} + +void LFProcessor::poll() { + apr_status_t status = APR_EGENERAL; + do{ + current = 0; + if(!stopped){ + status = apr_pollset_poll(pollset, timeout, &signalledCount, &signalledFDs); + } + }while(status != APR_SUCCESS && !stopped); +} + +void LFProcessor::run(){ + try{ + while(!stopped){ + const apr_pollfd_t* event = 0; + LFSessionContext* session = 0; + { + Monitor::ScopedLock l(leadLock); + waitToLead(); + event = getNextEvent(); + if(!event) return; + session = reinterpret_cast<LFSessionContext*>( + event->client_data); + session->startProcessing(); + relinquishLead(); + } + + //process event: + if(event->rtnevents & APR_POLLIN) session->read(); + if(event->rtnevents & APR_POLLOUT) session->write(); + + if(session->isClosed()){ + session->handleClose(); + Monitor::ScopedLock l(countLock); + sessions.erase(find(sessions.begin(),sessions.end(), session)); + count--; + }else{ + session->stopProcessing(); + } + } + }catch(std::exception e){ + std::cout << e.what() << std::endl; + } +} + +void LFProcessor::waitToLead(){ + while(hasLeader && !stopped) leadLock.wait(); + hasLeader = !stopped; +} + +void LFProcessor::relinquishLead(){ + hasLeader = false; + leadLock.notify(); +} + +const apr_pollfd_t* LFProcessor::getNextEvent(){ + while(true){ + if(stopped){ + return 0; + }else if(current < signalledCount){ + //use result of previous poll if one is available + return signalledFDs + (current++); + }else{ + //else poll to get new events + poll(); + } + } +} + +void LFProcessor::stop(){ + stopped = true; + { + Monitor::ScopedLock l(leadLock); + leadLock.notifyAll(); + } + for(int i = 0; i < workerCount; i++){ + workers[i].join(); + } + for(iterator i = sessions.begin(); i < sessions.end(); i++){ + (*i)->shutdown(); + } +} + diff --git a/cpp/lib/common/sys/apr/LFProcessor.h b/cpp/lib/common/sys/apr/LFProcessor.h new file mode 100644 index 0000000000..de90199472 --- /dev/null +++ b/cpp/lib/common/sys/apr/LFProcessor.h @@ -0,0 +1,121 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _LFProcessor_ +#define _LFProcessor_ + +#include <apr_poll.h> +#include <iostream> +#include <vector> +#include <sys/Monitor.h> +#include <sys/Runnable.h> +#include <sys/Thread.h> + +namespace qpid { +namespace sys { + + class LFSessionContext; + + /** + * This class processes a poll set using the leaders-followers + * pattern for thread synchronization: the leader will poll and on + * the poll returning, it will remove a session, promote a + * follower to leadership, then process the session. + */ + class LFProcessor : private virtual qpid::sys::Runnable + { + typedef std::vector<LFSessionContext*>::iterator iterator; + + const int size; + const apr_interval_time_t timeout; + apr_pollset_t* pollset; + int signalledCount; + int current; + const apr_pollfd_t* signalledFDs; + int count; + const int workerCount; + bool hasLeader; + qpid::sys::Thread* workers; + qpid::sys::Monitor leadLock; + qpid::sys::Mutex countLock; + std::vector<LFSessionContext*> sessions; + volatile bool stopped; + + const apr_pollfd_t* getNextEvent(); + void waitToLead(); + void relinquishLead(); + void poll(); + virtual void run(); + + public: + LFProcessor(apr_pool_t* pool, int workers, int size, int timeout); + /** + * Add the fd to the poll set. Relies on the client_data being + * an instance of LFSessionContext. + */ + void add(const apr_pollfd_t* const fd); + /** + * Remove the fd from the poll set. + */ + void remove(const apr_pollfd_t* const fd); + /** + * Signal that the fd passed in, already part of the pollset, + * has had its flags altered. + */ + void update(const apr_pollfd_t* const fd); + /** + * Add an fd back to the poll set after deactivation. + */ + void reactivate(const apr_pollfd_t* const fd); + /** + * Temporarily remove the fd from the poll set. Called when processing + * is about to begin. + */ + void deactivate(const apr_pollfd_t* const fd); + /** + * Indicates whether the capacity of this processor has been + * reached (or whether it can still handle further fd's). + */ + bool full(); + /** + * Indicates whether there are any fd's registered. + */ + bool empty(); + /** + * Stop processing. + */ + void stop(); + /** + * Start processing. + */ + void start(); + /** + * Is processing stopped? + */ + bool isStopped(); + + ~LFProcessor(); + }; + +} +} + + +#endif diff --git a/cpp/lib/common/sys/apr/LFSessionContext.cpp b/cpp/lib/common/sys/apr/LFSessionContext.cpp new file mode 100644 index 0000000000..503dfddbb7 --- /dev/null +++ b/cpp/lib/common/sys/apr/LFSessionContext.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 "LFSessionContext.h" +#include "APRBase.h" +#include <QpidError.h> +#include <assert.h> + +using namespace qpid::sys; +using namespace qpid::sys; +using namespace qpid::framing; + +LFSessionContext::LFSessionContext(apr_pool_t* _pool, apr_socket_t* _socket, + LFProcessor* const _processor, + bool _debug) : + debug(_debug), + socket(_socket), + initiated(false), + in(65536), + out(65536), + processor(_processor), + processing(false), + closing(false) +{ + + fd.p = _pool; + fd.desc_type = APR_POLL_SOCKET; + fd.reqevents = APR_POLLIN; + fd.client_data = this; + fd.desc.s = _socket; + + out.flip(); +} + +LFSessionContext::~LFSessionContext(){ + +} + +void LFSessionContext::read(){ + socket.read(in); + in.flip(); + if(initiated){ + AMQFrame frame; + try{ + while(frame.decode(in)){ + if(debug) log("RECV", &frame); + handler->received(&frame); + } + }catch(QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg + << " (" << error.loc.file << ":" << error.loc.line + << ")" << std::endl; + } + }else{ + ProtocolInitiation protocolInit; + if(protocolInit.decode(in)){ + handler->initiated(&protocolInit); + initiated = true; + if(debug) std::cout << "INIT [" << &socket << "]" << std::endl; + } + } + in.compact(); +} + +void LFSessionContext::write(){ + bool done = isClosed(); + while(!done){ + if(out.available() > 0){ + socket.write(out); + if(out.available() > 0){ + + //incomplete write, leave flags to receive notification of readiness to write + done = true;//finished processing for now, but write is still in progress + } + }else{ + //do we have any frames to write? + Mutex::ScopedLock l(writeLock); + if(!framesToWrite.empty()){ + out.clear(); + bool encoded(false); + AMQFrame* frame = framesToWrite.front(); + while(frame && out.available() >= frame->size()){ + encoded = true; + frame->encode(out); + if(debug) log("SENT", frame); + delete frame; + framesToWrite.pop(); + frame = framesToWrite.empty() ? 0 : framesToWrite.front(); + } + if(!encoded) THROW_QPID_ERROR(FRAMING_ERROR, "Could not write frame, too large for buffer."); + out.flip(); + }else{ + //reset flags, don't care about writability anymore + fd.reqevents = APR_POLLIN; + done = true; + + if(closing){ + socket.close(); + } + } + } + } +} + +void LFSessionContext::send(AMQFrame* frame){ + Mutex::ScopedLock l(writeLock); + if(!closing){ + framesToWrite.push(frame); + if(!(fd.reqevents & APR_POLLOUT)){ + fd.reqevents |= APR_POLLOUT; + if(!processing){ + processor->update(&fd); + } + } + } +} + +void LFSessionContext::startProcessing(){ + Mutex::ScopedLock l(writeLock); + processing = true; + processor->deactivate(&fd); +} + +void LFSessionContext::stopProcessing(){ + Mutex::ScopedLock l(writeLock); + processor->reactivate(&fd); + processing = false; +} + +void LFSessionContext::close(){ + Mutex::ScopedLock l(writeLock); + closing = true; + if(!processing){ + //allow pending frames to be written to socket + fd.reqevents = APR_POLLOUT; + processor->update(&fd); + } +} + +void LFSessionContext::handleClose(){ + handler->closed(); + std::cout << "Session closed [" << &socket << "]" << std::endl; + delete handler; + delete this; +} + +void LFSessionContext::shutdown(){ + socket.close(); + handleClose(); +} + +void LFSessionContext::init(ConnectionInputHandler* _handler){ + handler = _handler; + processor->add(&fd); +} + +void LFSessionContext::log(const std::string& desc, AMQFrame* const frame){ + Mutex::ScopedLock l(logLock); + std::cout << desc << " [" << &socket << "]: " << *frame << std::endl; +} + +Mutex LFSessionContext::logLock; diff --git a/cpp/lib/common/sys/apr/LFSessionContext.h b/cpp/lib/common/sys/apr/LFSessionContext.h new file mode 100644 index 0000000000..81cfc0efda --- /dev/null +++ b/cpp/lib/common/sys/apr/LFSessionContext.h @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#ifndef _LFSessionContext_ +#define _LFSessionContext_ + +#include <queue> + +#include <apr_network_io.h> +#include <apr_poll.h> +#include <apr_time.h> + +#include <AMQFrame.h> +#include <Buffer.h> +#include <sys/Monitor.h> +#include <sys/ConnectionOutputHandler.h> +#include <sys/ConnectionInputHandler.h> + +#include "APRSocket.h" +#include "LFProcessor.h" + +namespace qpid { +namespace sys { + + +class LFSessionContext : public virtual qpid::sys::ConnectionOutputHandler +{ + const bool debug; + APRSocket socket; + bool initiated; + + qpid::framing::Buffer in; + qpid::framing::Buffer out; + + qpid::sys::ConnectionInputHandler* handler; + LFProcessor* const processor; + + apr_pollfd_t fd; + + std::queue<qpid::framing::AMQFrame*> framesToWrite; + qpid::sys::Mutex writeLock; + + bool processing; + bool closing; + + static qpid::sys::Mutex logLock; + void log(const std::string& desc, + qpid::framing::AMQFrame* const frame); + + + public: + LFSessionContext(apr_pool_t* pool, apr_socket_t* socket, + LFProcessor* const processor, + bool debug = false); + virtual ~LFSessionContext(); + virtual void send(qpid::framing::AMQFrame* frame); + virtual void close(); + void read(); + void write(); + void init(qpid::sys::ConnectionInputHandler* handler); + void startProcessing(); + void stopProcessing(); + void handleClose(); + void shutdown(); + inline apr_pollfd_t* const getFd(){ return &fd; } + inline bool isClosed(){ return !socket.isOpen(); } +}; + +} +} + + +#endif diff --git a/cpp/lib/common/sys/apr/Socket.cpp b/cpp/lib/common/sys/apr/Socket.cpp new file mode 100644 index 0000000000..bca4da6c96 --- /dev/null +++ b/cpp/lib/common/sys/apr/Socket.cpp @@ -0,0 +1,86 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +#include <sys/Socket.h> +#include <apr/APRBase.h> +#include <apr/APRPool.h> + + +using namespace qpid::sys; + +Socket Socket::createTcp() { + Socket s; + CHECK_APR_SUCCESS( + apr_socket_create( + &s.socket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, + APRPool::get())); + return s; +} + +Socket::Socket(apr_socket_t* s) { + socket = s; +} + +void Socket::setTimeout(Time interval) { + apr_socket_timeout_set(socket, interval/TIME_USEC); +} + +void Socket::connect(const std::string& host, int port) { + apr_sockaddr_t* address; + CHECK_APR_SUCCESS( + apr_sockaddr_info_get( + &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, + APRPool::get())); + CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); +} + +void Socket::close() { + if (socket == 0) return; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + socket = 0; +} + +ssize_t Socket::send(const void* data, size_t size) +{ + apr_size_t sent = size; + apr_status_t status = + apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent); + if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return sent; +} + +ssize_t Socket::recv(void* data, size_t size) +{ + apr_size_t received = size; + apr_status_t status = + apr_socket_recv(socket, reinterpret_cast<char*>(data), &received); + if (APR_STATUS_IS_TIMEUP(status)) + return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) + return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return received; +} + + diff --git a/cpp/lib/common/sys/apr/Thread.cpp b/cpp/lib/common/sys/apr/Thread.cpp new file mode 100644 index 0000000000..5c4799aa96 --- /dev/null +++ b/cpp/lib/common/sys/apr/Thread.cpp @@ -0,0 +1,33 @@ +/* + * + * 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 <sys/Thread.h> + +using namespace qpid::sys; +using qpid::sys::Runnable; + +void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) { + reinterpret_cast<Runnable*>(data)->run(); + CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); + return NULL; +} + + diff --git a/cpp/lib/common/sys/posix/EventChannel.cpp b/cpp/lib/common/sys/posix/EventChannel.cpp new file mode 100644 index 0000000000..16c7ec9c3f --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannel.cpp @@ -0,0 +1,325 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <mqueue.h> +#include <string.h> +#include <iostream> + +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/epoll.h> + +#include <typeinfo> +#include <iostream> +#include <queue> + +#include <boost/ptr_container/ptr_map.hpp> +#include <boost/current_function.hpp> + +#include <QpidError.h> +#include <sys/Monitor.h> + +#include "check.h" +#include "EventChannel.h" + +using namespace std; + + +// Convenience template to zero out a struct. +template <class S> struct ZeroStruct : public S { + ZeroStruct() { memset(this, 0, sizeof(*this)); } +}; + +namespace qpid { +namespace sys { + + +/** + * EventHandler wraps an epoll file descriptor. Acts as private + * interface between EventChannel and subclasses. + * + * Also implements Event interface for events that are not associated + * with a file descriptor and are passed via the message queue. + */ +class EventHandler : public Event, private Monitor +{ + public: + EventHandler(int epollSize = 256); + ~EventHandler(); + + int getEpollFd() { return epollFd; } + void epollAdd(int fd, uint32_t epollEvents, Event* event); + void epollMod(int fd, uint32_t epollEvents, Event* event); + void epollDel(int fd); + + void mqPut(Event* event); + Event* mqGet(); + + protected: + // Should never be called, only complete. + void prepare(EventHandler&) { assert(0); } + Event* complete(EventHandler& eh); + + private: + int epollFd; + std::string mqName; + int mqFd; + std::queue<Event*> mqEvents; +}; + +EventHandler::EventHandler(int epollSize) +{ + epollFd = epoll_create(epollSize); + if (epollFd < 0) throw QPID_POSIX_ERROR(errno); + + // Create a POSIX message queue for non-fd events. + // We write one byte and never read it is always ready for read + // when we add it to epoll. + // + ZeroStruct<struct mq_attr> attr; + attr.mq_maxmsg = 1; + attr.mq_msgsize = 1; + do { + char tmpnam[L_tmpnam]; + tmpnam_r(tmpnam); + mqName = tmpnam + 4; // Skip "tmp/" + mqFd = mq_open( + mqName.c_str(), O_CREAT|O_EXCL|O_RDWR|O_NONBLOCK, S_IRWXU, &attr); + if (mqFd < 0) throw QPID_POSIX_ERROR(errno); + } while (mqFd == EEXIST); // Name already taken, try again. + + static char zero = '\0'; + mq_send(mqFd, &zero, 1, 0); + epollAdd(mqFd, 0, this); +} + +EventHandler::~EventHandler() { + mq_close(mqFd); + mq_unlink(mqName.c_str()); +} + +void EventHandler::mqPut(Event* event) { + ScopedLock l(*this); + assert(event != 0); + mqEvents.push(event); + epollMod(mqFd, EPOLLIN|EPOLLONESHOT, this); +} + +Event* EventHandler::mqGet() { + ScopedLock l(*this); + if (mqEvents.empty()) + return 0; + Event* event = mqEvents.front(); + mqEvents.pop(); + if(!mqEvents.empty()) + epollMod(mqFd, EPOLLIN|EPOLLONESHOT, this); + return event; +} + +void EventHandler::epollAdd(int fd, uint32_t epollEvents, Event* event) +{ + ZeroStruct<struct epoll_event> ee; + ee.data.ptr = event; + ee.events = epollEvents; + if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ee) < 0) + throw QPID_POSIX_ERROR(errno); +} + +void EventHandler::epollMod(int fd, uint32_t epollEvents, Event* event) +{ + ZeroStruct<struct epoll_event> ee; + ee.data.ptr = event; + ee.events = epollEvents; + if (epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &ee) < 0) + throw QPID_POSIX_ERROR(errno); +} + +void EventHandler::epollDel(int fd) { + if (epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, 0) < 0) + throw QPID_POSIX_ERROR(errno); +} + +Event* EventHandler::complete(EventHandler& eh) +{ + assert(&eh == this); + Event* event = mqGet(); + return event==0 ? 0 : event->complete(eh); +} + +// ================================================================ +// EventChannel + +EventChannel::shared_ptr EventChannel::create() { + return shared_ptr(new EventChannel()); +} + +EventChannel::EventChannel() : handler(new EventHandler()) {} + +EventChannel::~EventChannel() {} + +void EventChannel::postEvent(Event& e) +{ + e.prepare(*handler); +} + +Event* EventChannel::getEvent() +{ + static const int infiniteTimeout = -1; + ZeroStruct<struct epoll_event> epollEvent; + + // Loop until we can complete the event. Some events may re-post + // themselves and return 0 from complete, e.g. partial reads. // + Event* event = 0; + while (event == 0) { + int eventCount = epoll_wait(handler->getEpollFd(), + &epollEvent, 1, infiniteTimeout); + if (eventCount < 0) { + if (errno != EINTR) { + // TODO aconway 2006-11-28: Proper handling/logging of errors. + cerr << BOOST_CURRENT_FUNCTION << " ignoring error " + << PosixError::getMessage(errno) << endl; + assert(0); + } + } + else if (eventCount == 1) { + event = reinterpret_cast<Event*>(epollEvent.data.ptr); + assert(event != 0); + try { + event = event->complete(*handler); + } + catch (const Exception& e) { + if (event) + event->setError(e); + } + catch (const std::exception& e) { + if (event) + event->setError(e); + } + } + } + return event; +} + +Event::~Event() {} + +void Event::prepare(EventHandler& handler) +{ + handler.mqPut(this); +} + +bool Event::hasError() const { + return error; +} + +void Event::throwIfError() throw (Exception) { + if (hasError()) + error.throwSelf(); +} + +Event* Event::complete(EventHandler&) +{ + return this; +} + +void Event::dispatch() +{ + try { + if (!callback.empty()) + callback(); + } catch (const std::exception&) { + throw; + } catch (...) { + throw QPID_ERROR(INTERNAL_ERROR, "Unknown exception."); + } +} + +void Event::setError(const ExceptionHolder& e) { + error = e; +} + +void ReadEvent::prepare(EventHandler& handler) +{ + handler.epollAdd(descriptor, EPOLLIN | EPOLLONESHOT, this); +} + +ssize_t ReadEvent::doRead() { + ssize_t n = ::read(descriptor, static_cast<char*>(buffer) + received, + size - received); + if (n > 0) received += n; + return n; +} + +Event* ReadEvent::complete(EventHandler& handler) +{ + // Read as much as possible without blocking. + ssize_t n = doRead(); + while (n > 0 && received < size) doRead(); + + if (received == size) { + handler.epollDel(descriptor); + received = 0; // Reset for re-use. + return this; + } + else if (n <0 && (errno == EAGAIN)) { + // Keep polling for more. + handler.epollMod(descriptor, EPOLLIN | EPOLLONESHOT, this); + return 0; + } + else { + // Unexpected EOF or error. Throw ENODATA for EOF. + handler.epollDel(descriptor); + received = 0; // Reset for re-use. + throw QPID_POSIX_ERROR((n < 0) ? errno : ENODATA); + } +} + +void WriteEvent::prepare(EventHandler& handler) +{ + handler.epollAdd(descriptor, EPOLLOUT | EPOLLONESHOT, this); +} + +Event* WriteEvent::complete(EventHandler& handler) +{ + ssize_t n = write(descriptor, static_cast<const char*>(buffer) + written, + size - written); + if (n < 0) throw QPID_POSIX_ERROR(errno); + written += n; + if(written < size) { + // Keep polling. + handler.epollMod(descriptor, EPOLLOUT | EPOLLONESHOT, this); + return 0; + } + written = 0; // Reset for re-use. + handler.epollDel(descriptor); + return this; +} + +void AcceptEvent::prepare(EventHandler& handler) +{ + handler.epollAdd(descriptor, EPOLLIN | EPOLLONESHOT, this); +} + +Event* AcceptEvent::complete(EventHandler& handler) +{ + handler.epollDel(descriptor); + accepted = ::accept(descriptor, 0, 0); + if (accepted < 0) throw QPID_POSIX_ERROR(errno); + return this; +} + +}} diff --git a/cpp/lib/common/sys/posix/EventChannel.h b/cpp/lib/common/sys/posix/EventChannel.h new file mode 100644 index 0000000000..49c7fce740 --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannel.h @@ -0,0 +1,176 @@ +#ifndef _sys_EventChannel_h +#define _sys_EventChannel_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <SharedObject.h> +#include <ExceptionHolder.h> +#include <boost/function.hpp> +#include <memory> + +namespace qpid { +namespace sys { + +class Event; +class EventHandler; +class EventChannel; + +/** + * Base class for all Events. + */ +class Event +{ + public: + /** Type for callback when event is dispatched */ + typedef boost::function0<void> Callback; + + /** + * Create an event with optional callback. + * Instances of Event are sent directly through the channel. + * Derived classes define additional waiting behaviour. + *@param cb A callback functor that is invoked when dispatch() is called. + */ + Event(Callback cb = 0) : callback(cb) {} + + virtual ~Event(); + + /** Call the callback provided to the constructor, if any. */ + void dispatch(); + + /** True if there was an error processing this event */ + bool hasError() const; + + /** If hasError() throw the corresponding exception. */ + void throwIfError() throw(Exception); + + protected: + virtual void prepare(EventHandler&); + virtual Event* complete(EventHandler&); + void setError(const ExceptionHolder& e); + + Callback callback; + ExceptionHolder error; + + friend class EventChannel; + friend class EventHandler; +}; + +template <class BufT> +class IOEvent : public Event { + public: + void getDescriptor() const { return descriptor; } + size_t getSize() const { return size; } + BufT getBuffer() const { return buffer; } + + protected: + IOEvent(int fd, Callback cb, size_t sz, BufT buf) : + Event(cb), descriptor(fd), buffer(buf), size(sz) {} + + int descriptor; + BufT buffer; + size_t size; +}; + +/** Asynchronous read event */ +class ReadEvent : public IOEvent<void*> +{ + public: + explicit ReadEvent(int fd=-1, void* buf=0, size_t sz=0, Callback cb=0) : + IOEvent<void*>(fd, cb, sz, buf), received(0) {} + + private: + void prepare(EventHandler&); + Event* complete(EventHandler&); + ssize_t doRead(); + + size_t received; +}; + +/** Asynchronous write event */ +class WriteEvent : public IOEvent<const void*> +{ + public: + explicit WriteEvent(int fd=-1, const void* buf=0, size_t sz=0, + Callback cb=0) : + IOEvent<const void*>(fd, cb, sz, buf), written(0) {} + + protected: + void prepare(EventHandler&); + Event* complete(EventHandler&); + + private: + ssize_t doWrite(); + size_t written; +}; + +/** Asynchronous socket accept event */ +class AcceptEvent : public Event +{ + public: + /** Accept a connection on fd. */ + explicit AcceptEvent(int fd=-1, Callback cb=0) : + Event(cb), descriptor(fd), accepted(0) {} + + /** Get descriptor for server socket */ + int getAcceptedDesscriptor() const { return accepted; } + + private: + void prepare(EventHandler&); + Event* complete(EventHandler&); + + int descriptor; + int accepted; +}; + + +class QueueSet; + +/** + * Channel to post and wait for events. + */ +class EventChannel : public qpid::SharedObject<EventChannel> +{ + public: + static shared_ptr create(); + + ~EventChannel(); + + /** Post an event to the channel. */ + void postEvent(Event& event); + + /** Post an event to the channel. Must not be 0. */ + void postEvent(Event* event) { postEvent(*event); } + + /** + * Wait for the next complete event. + *@return Pointer to event. Will never return 0. + */ + Event* getEvent(); + + private: + EventChannel(); + boost::shared_ptr<EventHandler> handler; +}; + + +}} + + + +#endif /*!_sys_EventChannel_h*/ diff --git a/cpp/lib/common/sys/posix/EventChannelAcceptor.cpp b/cpp/lib/common/sys/posix/EventChannelAcceptor.cpp new file mode 100644 index 0000000000..548fbd1881 --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannelAcceptor.cpp @@ -0,0 +1,149 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> + +#include <boost/assert.hpp> +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/ptr_container/ptr_deque.hpp> +#include <boost/bind.hpp> +#include <boost/scoped_ptr.hpp> + +#include <sys/ConnectionOutputHandler.h> +#include <sys/ConnectionInputHandler.h> +#include <sys/ConnectionInputHandlerFactory.h> +#include <sys/Acceptor.h> +#include <sys/Socket.h> +#include <framing/Buffer.h> +#include <framing/AMQFrame.h> +#include <Exception.h> + +#include "EventChannelConnection.h" + +namespace qpid { +namespace sys { + +using namespace qpid::framing; +using namespace std; + +class EventChannelAcceptor : public Acceptor { + public: + + + EventChannelAcceptor( + int16_t port_, int backlog, int nThreads, bool trace_ + ); + + int getPort() const; + + void run(ConnectionInputHandlerFactory& factory); + + void shutdown(); + + private: + + void accept(); + + Mutex lock; + Socket listener; + const int port; + const bool isTrace; + bool isRunning; + boost::ptr_vector<EventChannelConnection> connections; + AcceptEvent acceptEvent; + ConnectionInputHandlerFactory* factory; + bool isShutdown; + EventChannelThreads::shared_ptr threads; +}; + +Acceptor::shared_ptr Acceptor::create( + int16_t port, int backlog, int threads, bool trace) +{ + return Acceptor::shared_ptr( + new EventChannelAcceptor(port, backlog, threads, trace)); +} + +// Must define Acceptor virtual dtor. +Acceptor::~Acceptor() {} + +EventChannelAcceptor::EventChannelAcceptor( + int16_t port_, int backlog, int nThreads, bool trace_ +) : listener(Socket::createTcp()), + port(listener.listen(int(port_), backlog)), + isTrace(trace_), + isRunning(false), + acceptEvent(listener.fd(), + boost::bind(&EventChannelAcceptor::accept, this)), + factory(0), + isShutdown(false), + threads(EventChannelThreads::create(EventChannel::create(), nThreads)) +{ } + +int EventChannelAcceptor::getPort() const { + return port; // Immutable no need for lock. +} + +void EventChannelAcceptor::run(ConnectionInputHandlerFactory& f) { + { + Mutex::ScopedLock l(lock); + if (!isRunning && !isShutdown) { + isRunning = true; + factory = &f; + threads->post(acceptEvent); + } + } + threads->join(); // Wait for shutdown. +} + +void EventChannelAcceptor::shutdown() { + bool doShutdown = false; + { + Mutex::ScopedLock l(lock); + doShutdown = !isShutdown; // I'm the shutdown thread. + isShutdown = true; + } + if (doShutdown) { + ::close(acceptEvent.getDescriptor()); + threads->shutdown(); + for_each(connections.begin(), connections.end(), + boost::bind(&EventChannelConnection::close, _1)); + } + threads->join(); +} + +void EventChannelAcceptor::accept() +{ + // No lock, we only post one accept event at a time. + if (isShutdown) + return; + if (acceptEvent.getException()) { + Exception::log(*acceptEvent.getException(), + "EventChannelAcceptor::accept"); + shutdown(); + return; + } + // TODO aconway 2006-11-29: Need to reap closed connections also. + int fd = acceptEvent.getAcceptedDesscriptor(); + connections.push_back( + new EventChannelConnection(threads, *factory, fd, fd, isTrace)); + threads->post(acceptEvent); // Keep accepting. +} + +}} // namespace qpid::sys diff --git a/cpp/lib/common/sys/posix/EventChannelConnection.cpp b/cpp/lib/common/sys/posix/EventChannelConnection.cpp new file mode 100644 index 0000000000..4449dc3035 --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannelConnection.cpp @@ -0,0 +1,229 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <iostream> + +#include <boost/bind.hpp> +#include <boost/assert.hpp> + +#include "EventChannelConnection.h" +#include "sys/ConnectionInputHandlerFactory.h" +#include "QpidError.h" + +using namespace std; +using namespace qpid; +using namespace qpid::framing; + +namespace qpid { +namespace sys { + +const size_t EventChannelConnection::bufferSize = 65536; + +EventChannelConnection::EventChannelConnection( + EventChannelThreads::shared_ptr threads_, + ConnectionInputHandlerFactory& factory_, + int rfd, + int wfd, + bool isTrace_ +) : + readFd(rfd), + writeFd(wfd ? wfd : rfd), + readCallback(boost::bind(&EventChannelConnection::closeOnException, + this, &EventChannelConnection::endInitRead)), + + isWriting(false), + isClosed(false), + threads(threads_), + handler(factory_.create(this)), + in(bufferSize), + out(bufferSize), + isTrace(isTrace_) +{ + BOOST_ASSERT(readFd > 0); + BOOST_ASSERT(writeFd > 0); + closeOnException(&EventChannelConnection::startRead); +} + + +void EventChannelConnection::send(std::auto_ptr<AMQFrame> frame) { + { + Monitor::ScopedLock lock(monitor); + assert(frame.get()); + writeFrames.push_back(frame.release()); + } + closeOnException(&EventChannelConnection::startWrite); +} + +void EventChannelConnection::close() { + { + Monitor::ScopedLock lock(monitor); + if (isClosed) + return; + isClosed = true; + } + ::close(readFd); + ::close(writeFd); + { + Monitor::ScopedLock lock(monitor); + while (busyThreads > 0) + monitor.wait(); + } + handler->closed(); +} + +void EventChannelConnection::closeNoThrow() { + Exception::tryCatchLog<void>( + boost::bind(&EventChannelConnection::close, this), + false, + "Exception closing channel" + ); +} + +/** + * Call f in a try/catch block and close the connection if + * an exception is thrown. + */ +void EventChannelConnection::closeOnException(MemberFnPtr f) +{ + try { + Exception::tryCatchLog<void>( + boost::bind(f, this), + "Closing connection due to exception" + ); + return; + } catch (...) { + // Exception was already logged by tryCatchLog + closeNoThrow(); + } +} + +// Post the write event. +// Always called inside closeOnException. +// Called by endWrite and send, but only one thread writes at a time. +// +void EventChannelConnection::startWrite() { + FrameQueue::auto_type frame; + { + Monitor::ScopedLock lock(monitor); + // Stop if closed or a write event is already in progress. + if (isClosed || isWriting) + return; + if (writeFrames.empty()) { + isWriting = false; + return; + } + isWriting = true; + frame = writeFrames.pop_front(); + } + // No need to lock here - only one thread can be writing at a time. + out.clear(); + if (isTrace) + cout << "Send on socket " << writeFd << ": " << *frame << endl; + frame->encode(out); + out.flip(); + writeEvent = WriteEvent( + writeFd, out.start(), out.available(), + boost::bind(&EventChannelConnection::closeOnException, + this, &EventChannelConnection::endWrite)); + threads->post(writeEvent); +} + +// ScopedBusy ctor increments busyThreads. +// dtor decrements and calls monitor.notifyAll if it reaches 0. +// +struct EventChannelConnection::ScopedBusy : public AtomicCount::ScopedIncrement +{ + ScopedBusy(EventChannelConnection& ecc) + : AtomicCount::ScopedIncrement( + ecc.busyThreads, boost::bind(&Monitor::notifyAll, &ecc.monitor)) + {} +}; + +// Write event completed. +// Always called by a channel thread inside closeOnException. +// +void EventChannelConnection::endWrite() { + ScopedBusy(*this); + { + Monitor::ScopedLock lock(monitor); + isWriting = false; + if (isClosed) + return; + writeEvent.throwIfException(); + } + // Check if there's more in to write in the write queue. + startWrite(); +} + + +// Post the read event. +// Always called inside closeOnException. +// Called from ctor and end[Init]Read, so only one call at a time +// is possible since we only post one read event at a time. +// +void EventChannelConnection::startRead() { + // Non blocking read, as much as we can swallow. + readEvent = ReadEvent( + readFd, in.start(), in.available(), readCallback,true); + threads->post(readEvent); +} + +// Completion of initial read, expect protocolInit. +// Always called inside closeOnException in channel thread. +// Only called by one thread at a time. +void EventChannelConnection::endInitRead() { + ScopedBusy(*this); + if (!isClosed) { + readEvent.throwIfException(); + in.move(readEvent.getBytesRead()); + in.flip(); + ProtocolInitiation protocolInit; + if(protocolInit.decode(in)){ + handler->initiated(&protocolInit); + readCallback = boost::bind( + &EventChannelConnection::closeOnException, + this, &EventChannelConnection::endRead); + } + in.compact(); + // Continue reading. + startRead(); + } +} + +// Normal reads, expect a frame. +// Always called inside closeOnException in channel thread. +void EventChannelConnection::endRead() { + ScopedBusy(*this); + if (!isClosed) { + readEvent.throwIfException(); + in.move(readEvent.getBytesRead()); + in.flip(); + AMQFrame frame; + while (frame.decode(in)) { + // TODO aconway 2006-11-30: received should take Frame& + if (isTrace) + cout << "Received on socket " << readFd + << ": " << frame << endl; + handler->received(&frame); + } + in.compact(); + startRead(); + } +} + +}} // namespace qpid::sys diff --git a/cpp/lib/common/sys/posix/EventChannelConnection.h b/cpp/lib/common/sys/posix/EventChannelConnection.h new file mode 100644 index 0000000000..da7b6dca27 --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannelConnection.h @@ -0,0 +1,102 @@ +#ifndef _posix_EventChannelConnection_h +#define _posix_EventChannelConnection_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <boost/ptr_container/ptr_deque.hpp> + +#include "EventChannelThreads.h" +#include "sys/Monitor.h" +#include "sys/ConnectionOutputHandler.h" +#include "sys/ConnectionInputHandler.h" +#include "sys/AtomicCount.h" +#include "framing/AMQFrame.h" + +namespace qpid { +namespace sys { + +class ConnectionInputHandlerFactory; + +/** + * Implements ConnectionOutputHandler and delegates to a ConnectionInputHandler + * for a connection via the EventChannel. + *@param readDescriptor file descriptor for reading. + *@param writeDescriptor file descriptor for writing, + * by default same as readDescriptor + */ +class EventChannelConnection : public ConnectionOutputHandler { + public: + EventChannelConnection( + EventChannelThreads::shared_ptr threads, + ConnectionInputHandlerFactory& factory, + int readDescriptor, + int writeDescriptor = 0, + bool isTrace = false + ); + + // TODO aconway 2006-11-30: ConnectionOutputHandler::send should take auto_ptr + virtual void send(qpid::framing::AMQFrame* frame) { + send(std::auto_ptr<qpid::framing::AMQFrame>(frame)); + } + + virtual void send(std::auto_ptr<qpid::framing::AMQFrame> frame); + + virtual void close(); + + private: + typedef boost::ptr_deque<qpid::framing::AMQFrame> FrameQueue; + typedef void (EventChannelConnection::*MemberFnPtr)(); + struct ScopedBusy; + + void startWrite(); + void endWrite(); + void startRead(); + void endInitRead(); + void endRead(); + void closeNoThrow(); + void closeOnException(MemberFnPtr); + bool shouldContinue(bool& flag); + + static const size_t bufferSize; + + Monitor monitor; + + int readFd, writeFd; + ReadEvent readEvent; + WriteEvent writeEvent; + Event::Callback readCallback; + bool isWriting; + bool isClosed; + AtomicCount busyThreads; + + EventChannelThreads::shared_ptr threads; + std::auto_ptr<ConnectionInputHandler> handler; + qpid::framing::Buffer in, out; + FrameQueue writeFrames; + bool isTrace; + + friend struct ScopedBusy; +}; + + +}} // namespace qpid::sys + + + +#endif /*!_posix_EventChannelConnection_h*/ diff --git a/cpp/lib/common/sys/posix/EventChannelThreads.cpp b/cpp/lib/common/sys/posix/EventChannelThreads.cpp new file mode 100644 index 0000000000..95e699e0b0 --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannelThreads.cpp @@ -0,0 +1,119 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "EventChannelThreads.h" +#include <sys/Runnable.h> +#include <iostream> +using namespace std; +#include <boost/bind.hpp> + +namespace qpid { +namespace sys { + +EventChannelThreads::shared_ptr EventChannelThreads::create( + EventChannel::shared_ptr ec) +{ + return EventChannelThreads::shared_ptr(new EventChannelThreads(ec)); +} + +EventChannelThreads::EventChannelThreads(EventChannel::shared_ptr ec) : + channel(ec), nWaiting(0), state(RUNNING) +{ + // TODO aconway 2006-11-15: Estimate initial threads based on CPUs. + addThread(); +} + +EventChannelThreads::~EventChannelThreads() { + shutdown(); + join(); +} + +void EventChannelThreads::shutdown() +{ + ScopedLock lock(*this); + if (state != RUNNING) // Already shutting down. + return; + for (size_t i = 0; i < workers.size(); ++i) { + channel->postEvent(terminate); + } + state = TERMINATE_SENT; + notify(); // Wake up one join() thread. +} + +void EventChannelThreads::join() +{ + { + ScopedLock lock(*this); + while (state == RUNNING) // Wait for shutdown to start. + wait(); + if (state == SHUTDOWN) // Shutdown is complete + return; + if (state == JOINING) { + // Someone else is doing the join. + while (state != SHUTDOWN) + wait(); + return; + } + // I'm the joining thread + assert(state == TERMINATE_SENT); + state = JOINING; + } // Drop the lock. + + for (size_t i = 0; i < workers.size(); ++i) { + assert(state == JOINING); // Only this thread can change JOINING. + workers[i].join(); + } + state = SHUTDOWN; + notifyAll(); // Notify other join() threaeds. +} + +void EventChannelThreads::addThread() { + ScopedLock l(*this); + workers.push_back(Thread(*this)); +} + +void EventChannelThreads::run() +{ + // Start life waiting. Decrement on exit. + AtomicCount::ScopedIncrement inc(nWaiting); + try { + while (true) { + Event* e = channel->getEvent(); + assert(e != 0); + if (e == &terminate) { + return; + } + AtomicCount::ScopedDecrement dec(nWaiting); + // I'm no longer waiting, make sure someone is. + if (dec == 0) + addThread(); + e->dispatch(); + } + } + catch (const std::exception& e) { + // TODO aconway 2006-11-15: need better logging across the board. + std::cerr << "EventChannelThreads::run() caught: " << e.what() + << std::endl; + } + catch (...) { + std::cerr << "EventChannelThreads::run() caught unknown exception." + << std::endl; + } +} + +}} diff --git a/cpp/lib/common/sys/posix/EventChannelThreads.h b/cpp/lib/common/sys/posix/EventChannelThreads.h new file mode 100644 index 0000000000..98403c0869 --- /dev/null +++ b/cpp/lib/common/sys/posix/EventChannelThreads.h @@ -0,0 +1,92 @@ +#ifndef _posix_EventChannelThreads_h +#define _sys_EventChannelThreads_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include <vector> + +#include <Exception.h> +#include <sys/Time.h> +#include <sys/Monitor.h> +#include <sys/Thread.h> +#include <sys/AtomicCount.h> +#include "EventChannel.h" + +namespace qpid { +namespace sys { + +/** + Dynamic thread pool serving an EventChannel. + + Threads run a loop { e = getEvent(); e->dispatch(); } + The size of the thread pool is automatically adjusted to optimal size. +*/ +class EventChannelThreads : + public qpid::SharedObject<EventChannelThreads>, + public sys::Monitor, private sys::Runnable +{ + public: + /** Create the thread pool and start initial threads. */ + static EventChannelThreads::shared_ptr create( + EventChannel::shared_ptr channel + ); + + ~EventChannelThreads(); + + /** Post event to the underlying channel */ + void postEvent(Event& event) { channel->postEvent(event); } + + /** Post event to the underlying channel Must not be 0. */ + void postEvent(Event* event) { channel->postEvent(event); } + + /** + * Terminate all threads. + * + * Returns immediately, use join() to wait till all threads are + * shut down. + */ + void shutdown(); + + /** Wait for all threads to terminate. */ + void join(); + + private: + typedef std::vector<sys::Thread> Threads; + typedef enum { + RUNNING, TERMINATE_SENT, JOINING, SHUTDOWN + } State; + + EventChannelThreads(EventChannel::shared_ptr underlyingChannel); + void addThread(); + + void run(); + bool keepRunning(); + void adjustThreads(); + + EventChannel::shared_ptr channel; + Threads workers; + sys::AtomicCount nWaiting; + State state; + Event terminate; +}; + + +}} + + +#endif /*!_sys_EventChannelThreads_h*/ diff --git a/cpp/lib/common/sys/posix/PosixAcceptor.cpp b/cpp/lib/common/sys/posix/PosixAcceptor.cpp new file mode 100644 index 0000000000..a80a6c61f7 --- /dev/null +++ b/cpp/lib/common/sys/posix/PosixAcceptor.cpp @@ -0,0 +1,48 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include <sys/Acceptor.h> +#include <Exception.h> + +namespace qpid { +namespace sys { + +namespace { +void fail() { throw qpid::Exception("PosixAcceptor not implemented"); } +} + +class PosixAcceptor : public Acceptor { + public: + virtual int16_t getPort() const { fail(); return 0; } + virtual void run(qpid::sys::ConnectionInputHandlerFactory* ) { fail(); } + virtual void shutdown() { fail(); } +}; + +// Define generic Acceptor::create() to return APRAcceptor. + Acceptor::shared_ptr Acceptor::create(int16_t , int, int, bool) +{ + return Acceptor::shared_ptr(new PosixAcceptor()); +} + +// Must define Acceptor virtual dtor. +Acceptor::~Acceptor() {} + +}} diff --git a/cpp/lib/common/sys/posix/Socket.cpp b/cpp/lib/common/sys/posix/Socket.cpp new file mode 100644 index 0000000000..5bd13742f6 --- /dev/null +++ b/cpp/lib/common/sys/posix/Socket.cpp @@ -0,0 +1,118 @@ +/* + * + * 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 <sys/socket.h> +#include <sys/errno.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <boost/format.hpp> + +#include <QpidError.h> +#include <posix/check.h> +#include <sys/Socket.h> + +using namespace qpid::sys; + +Socket Socket::createTcp() +{ + int s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s < 0) throw QPID_POSIX_ERROR(errno); + return s; +} + +Socket::Socket(int descriptor) : socket(descriptor) {} + +void Socket::setTimeout(Time interval) +{ + struct timeval tv; + tv.tv_sec = interval/TIME_SEC; + tv.tv_usec = (interval%TIME_SEC)/TIME_USEC; + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); +} + +void Socket::connect(const std::string& host, int port) +{ + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + struct hostent* hp = gethostbyname ( host.c_str() ); + if (hp == 0) throw QPID_POSIX_ERROR(errno); + memcpy(&name.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); + if (::connect(socket, (struct sockaddr*)(&name), sizeof(name)) < 0) + throw QPID_POSIX_ERROR(errno); +} + +void +Socket::close() +{ + if (socket == 0) return; + if (::close(socket) < 0) throw QPID_POSIX_ERROR(errno); + socket = 0; +} + +ssize_t +Socket::send(const void* data, size_t size) +{ + ssize_t sent = ::send(socket, data, size, 0); + if (sent < 0) { + if (errno == ECONNRESET) return SOCKET_EOF; + if (errno == ETIMEDOUT) return SOCKET_TIMEOUT; + throw QPID_POSIX_ERROR(errno); + } + return sent; +} + +ssize_t +Socket::recv(void* data, size_t size) +{ + ssize_t received = ::recv(socket, data, size, 0); + if (received < 0) { + if (errno == ETIMEDOUT) return SOCKET_TIMEOUT; + throw QPID_POSIX_ERROR(errno); + } + return received; +} + +int Socket::listen(int port, int backlog) +{ + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) + throw QPID_POSIX_ERROR(errno); + if (::listen(socket, backlog) < 0) + throw QPID_POSIX_ERROR(errno); + + socklen_t namelen = sizeof(name); + if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) + throw QPID_POSIX_ERROR(errno); + + return ntohs(name.sin_port); +} + + +int Socket::fd() +{ + return socket; +} diff --git a/cpp/lib/common/sys/posix/Thread.cpp b/cpp/lib/common/sys/posix/Thread.cpp new file mode 100644 index 0000000000..f524799556 --- /dev/null +++ b/cpp/lib/common/sys/posix/Thread.cpp @@ -0,0 +1,28 @@ +/* + * + * 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 <sys/Thread.h> + +void* qpid::sys::Thread::runRunnable(void* p) +{ + static_cast<Runnable*>(p)->run(); + return 0; +} diff --git a/cpp/lib/common/sys/posix/check.cpp b/cpp/lib/common/sys/posix/check.cpp new file mode 100644 index 0000000000..408679caa8 --- /dev/null +++ b/cpp/lib/common/sys/posix/check.cpp @@ -0,0 +1,39 @@ +/* + * + * 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 <cerrno> +#include "check.h" + +namespace qpid { +namespace sys { + +std::string +PosixError::getMessage(int errNo) +{ + char buf[512]; + return std::string(strerror_r(errNo, buf, sizeof(buf))); +} + +PosixError::PosixError(int errNo, const qpid::SrcLine& loc) throw() + : qpid::QpidError(INTERNAL_ERROR + errNo, getMessage(errNo), loc) +{ } + +}} diff --git a/cpp/lib/common/sys/posix/check.h b/cpp/lib/common/sys/posix/check.h new file mode 100644 index 0000000000..57b5a5757c --- /dev/null +++ b/cpp/lib/common/sys/posix/check.h @@ -0,0 +1,62 @@ +#ifndef _posix_check_h +#define _posix_check_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 <cerrno> +#include <string> +#include <QpidError.h> + +namespace qpid { +namespace sys { + +/** + * Exception with message from errno. + */ +class PosixError : public qpid::QpidError +{ + public: + static std::string getMessage(int errNo); + + PosixError(int errNo, const qpid::SrcLine& location) throw(); + + ~PosixError() throw() {} + + int getErrNo() { return errNo; } + + Exception* clone() const throw() { return new PosixError(*this); } + + void throwSelf() const { throw *this; } + + private: + int errNo; +}; + +}} + +/** Create a PosixError for the current file/line and errno. */ +#define QPID_POSIX_ERROR(errNo) ::qpid::sys::PosixError(errNo, SRCLINE) + +/** Throw a posix error if errNo is non-zero */ +#define QPID_POSIX_THROW_IF(ERRNO) \ + if ((ERRNO) != 0) throw QPID_POSIX_ERROR((ERRNO)) +#endif /*!_posix_check_h*/ |