diff options
author | Gordon Sim <gsim@apache.org> | 2009-08-25 17:57:34 +0000 |
---|---|---|
committer | Gordon Sim <gsim@apache.org> | 2009-08-25 17:57:34 +0000 |
commit | 86484cac17437cd15f2d313a40a7362b2ced6000 (patch) | |
tree | 873b08c2d3410adb7975ce1ebf0f52a6a72b83ea /qpid/cpp/src | |
parent | 1223c12c3501a2659a250e44917620986064d4f5 (diff) | |
download | qpid-python-86484cac17437cd15f2d313a40a7362b2ced6000.tar.gz |
QPID-664: Initial checkin of high level messaging api for c++
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@807731 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/cpp/src')
37 files changed, 4291 insertions, 3 deletions
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index a0f177dbd7..7456401618 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -523,6 +523,35 @@ set (libqpidclient_SOURCES qpid/client/SubscriptionImpl.cpp qpid/client/SubscriptionManager.cpp qpid/client/SubscriptionManagerImpl.cpp + qpid/messaging/Address.cpp + qpid/messaging/Connection.cpp + qpid/messaging/ConnectionImpl.h + qpid/messaging/Filter.cpp + qpid/messaging/Message.cpp + qpid/messaging/Receiver.cpp + qpid/messaging/ReceiverImpl.h + qpid/messaging/Session.cpp + qpid/messaging/SessionImpl.h + qpid/messaging/Sender.cpp + qpid/messaging/SenderImpl.h + qpid/messaging/Variant.cpp + qpid/client/amqp0_10/AddressResolution.h + qpid/client/amqp0_10/AddressResolution.cpp + qpid/client/amqp0_10/Codecs.cpp + qpid/client/amqp0_10/CompletionTracker.h + qpid/client/amqp0_10/CompletionTracker.cpp + qpid/client/amqp0_10/ConnectionImpl.h + qpid/client/amqp0_10/ConnectionImpl.cpp + qpid/client/amqp0_10/IncomingMessages.h + qpid/client/amqp0_10/IncomingMessages.cpp + qpid/client/amqp0_10/MessageSink.h + qpid/client/amqp0_10/MessageSource.h + qpid/client/amqp0_10/ReceiverImpl.h + qpid/client/amqp0_10/ReceiverImpl.cpp + qpid/client/amqp0_10/SessionImpl.h + qpid/client/amqp0_10/SessionImpl.cpp + qpid/client/amqp0_10/SenderImpl.h + qpid/client/amqp0_10/SenderImpl.cpp ) add_library (qpidclient SHARED ${libqpidclient_SOURCES}) target_link_libraries (qpidclient qpidcommon) diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am index 369c56a342..05b5efc5b5 100644 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@ -681,7 +681,36 @@ libqpidclient_la_SOURCES = \ qpid/client/SubscriptionImpl.h \ qpid/client/SubscriptionManager.cpp \ qpid/client/SubscriptionManagerImpl.cpp \ - qpid/client/SubscriptionManagerImpl.h + qpid/client/SubscriptionManagerImpl.h \ + qpid/messaging/Address.cpp \ + qpid/messaging/Connection.cpp \ + qpid/messaging/Filter.cpp \ + qpid/messaging/Message.cpp \ + qpid/messaging/Sender.cpp \ + qpid/messaging/Receiver.cpp \ + qpid/messaging/Session.cpp \ + qpid/messaging/Variant.cpp \ + qpid/messaging/ConnectionImpl.h \ + qpid/messaging/SenderImpl.h \ + qpid/messaging/ReceiverImpl.h \ + qpid/messaging/SessionImpl.h \ + qpid/client/amqp0_10/AddressResolution.h \ + qpid/client/amqp0_10/AddressResolution.cpp \ + qpid/client/amqp0_10/Codecs.cpp \ + qpid/client/amqp0_10/ConnectionImpl.h \ + qpid/client/amqp0_10/ConnectionImpl.cpp \ + qpid/client/amqp0_10/CompletionTracker.h \ + qpid/client/amqp0_10/CompletionTracker.cpp \ + qpid/client/amqp0_10/IncomingMessages.h \ + qpid/client/amqp0_10/IncomingMessages.cpp \ + qpid/client/amqp0_10/MessageSink.h \ + qpid/client/amqp0_10/MessageSource.h \ + qpid/client/amqp0_10/ReceiverImpl.h \ + qpid/client/amqp0_10/ReceiverImpl.cpp \ + qpid/client/amqp0_10/SessionImpl.h \ + qpid/client/amqp0_10/SessionImpl.cpp \ + qpid/client/amqp0_10/SenderImpl.h \ + qpid/client/amqp0_10/SenderImpl.cpp # NOTE: only public header files (which should be in ../include) # should go in this list. Private headers should go in the SOURCES @@ -751,7 +780,19 @@ nobase_include_HEADERS += \ ../include/qpid/sys/SystemInfo.h \ ../include/qpid/sys/Thread.h \ ../include/qpid/sys/Time.h \ - ../include/qpid/sys/uuid.h + ../include/qpid/sys/uuid.h \ + ../include/qpid/messaging/Address.h \ + ../include/qpid/messaging/Connection.h \ + ../include/qpid/messaging/Codec.h \ + ../include/qpid/messaging/Filter.h \ + ../include/qpid/messaging/Message.h \ + ../include/qpid/messaging/MessageContent.h \ + ../include/qpid/messaging/MessageListener.h \ + ../include/qpid/messaging/Sender.h \ + ../include/qpid/messaging/Receiver.h \ + ../include/qpid/messaging/Session.h \ + ../include/qpid/messaging/Variant.h \ + ../include/qpid/client/amqp0_10/Codecs.h # Force build of qpidd during dist phase so help2man will work. dist-hook: $(BUILT_SOURCES) diff --git a/qpid/cpp/src/qpid/client/SessionImpl.cpp b/qpid/cpp/src/qpid/client/SessionImpl.cpp index a617335370..8ead44a172 100644 --- a/qpid/cpp/src/qpid/client/SessionImpl.cpp +++ b/qpid/cpp/src/qpid/client/SessionImpl.cpp @@ -202,6 +202,16 @@ bool SessionImpl::isCompleteUpTo(const SequenceNumber& id) return f.result; } +framing::SequenceNumber SessionImpl::getCompleteUpTo() +{ + SequenceNumber firstIncomplete; + { + Lock l(state); + firstIncomplete = incompleteIn.front(); + } + return --firstIncomplete; +} + struct MarkCompleted { const SequenceNumber& id; diff --git a/qpid/cpp/src/qpid/client/SessionImpl.h b/qpid/cpp/src/qpid/client/SessionImpl.h index 3659450236..49d268c44d 100644 --- a/qpid/cpp/src/qpid/client/SessionImpl.h +++ b/qpid/cpp/src/qpid/client/SessionImpl.h @@ -103,6 +103,7 @@ public: void markCompleted(const framing::SequenceSet& ids, bool notifyPeer); bool isComplete(const framing::SequenceNumber& id); bool isCompleteUpTo(const framing::SequenceNumber& id); + framing::SequenceNumber getCompleteUpTo(); void waitForCompletion(const framing::SequenceNumber& id); void sendCompletion(); void sendFlush(); diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp new file mode 100644 index 0000000000..6ff9c2397a --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp @@ -0,0 +1,464 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/amqp0_10/MessageSource.h" +#include "qpid/client/amqp0_10/MessageSink.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/enum.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/ReplyTo.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::Exception; +using qpid::messaging::Address; +using qpid::messaging::Filter; +using qpid::messaging::Variant; +using qpid::framing::FieldTable; +using qpid::framing::ReplyTo; +using namespace qpid::framing::message; + + +namespace{ +const Variant EMPTY_VARIANT; +const FieldTable EMPTY_FIELD_TABLE; +const std::string EMPTY_STRING; + +//option names +const std::string BROWSE("browse"); +const std::string EXCLUSIVE("exclusive"); +const std::string MODE("mode"); +const std::string NAME("name"); +const std::string UNACKNOWLEDGED("unacknowledged"); + +const std::string QUEUE_ADDRESS("queue"); +const std::string TOPIC_ADDRESS("topic"); +const std::string TOPIC_ADDRESS_AND_SUBJECT("topic+"); +const std::string DIVIDER("/"); + +const std::string SIMPLE_SUBSCRIPTION("simple"); +const std::string RELIABLE_SUBSCRIPTION("reliable"); +const std::string DURABLE_SUBSCRIPTION("durable"); +} + +class QueueSource : public MessageSource +{ + public: + QueueSource(const std::string& name, AcceptMode=ACCEPT_MODE_EXPLICIT, AcquireMode=ACQUIRE_MODE_PRE_ACQUIRED, + bool exclusive = false, const FieldTable& options = EMPTY_FIELD_TABLE); + void subscribe(qpid::client::AsyncSession& session, const std::string& destination); + void cancel(qpid::client::AsyncSession& session, const std::string& destination); + private: + const std::string name; + const AcceptMode acceptMode; + const AcquireMode acquireMode; + const bool exclusive; + const FieldTable options; +}; + +class Subscription : public MessageSource +{ + public: + enum SubscriptionMode {SIMPLE, RELIABLE, DURABLE}; + + Subscription(const std::string& name, SubscriptionMode mode = SIMPLE, + const FieldTable& queueOptions = EMPTY_FIELD_TABLE, const FieldTable& subscriptionOptions = EMPTY_FIELD_TABLE); + void add(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE); + void subscribe(qpid::client::AsyncSession& session, const std::string& destination); + void cancel(qpid::client::AsyncSession& session, const std::string& destination); + + static SubscriptionMode getMode(const std::string& mode); + private: + struct Binding + { + Binding(const std::string& exchange, const std::string& key, const FieldTable& options = EMPTY_FIELD_TABLE); + + std::string exchange; + std::string key; + FieldTable options; + }; + + typedef std::vector<Binding> Bindings; + + const std::string name; + const bool autoDelete; + const bool durable; + const FieldTable queueOptions; + const FieldTable subscriptionOptions; + Bindings bindings; + std::string queue; +}; + +class Exchange : public MessageSink +{ + public: + Exchange(const std::string& name, const std::string& defaultSubject = EMPTY_STRING, + bool passive = true, const std::string& type = EMPTY_STRING, bool durable = false, + const FieldTable& options = EMPTY_FIELD_TABLE); + void declare(qpid::client::AsyncSession& session, const std::string& name); + void send(qpid::client::AsyncSession& session, const std::string& name, qpid::messaging::Message& message); + void cancel(qpid::client::AsyncSession& session, const std::string& name); + private: + const std::string name; + const std::string defaultSubject; + const bool passive; + const std::string type; + const bool durable; + const FieldTable options; +}; + +class QueueSink : public MessageSink +{ + public: + QueueSink(const std::string& name, bool passive=true, bool exclusive=false, + bool autoDelete=false, bool durable=false, const FieldTable& options = EMPTY_FIELD_TABLE); + void declare(qpid::client::AsyncSession& session, const std::string& name); + void send(qpid::client::AsyncSession& session, const std::string& name, qpid::messaging::Message& message); + void cancel(qpid::client::AsyncSession& session, const std::string& name); + private: + const std::string name; + const bool passive; + const bool exclusive; + const bool autoDelete; + const bool durable; + const FieldTable options; +}; + + +bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address); +bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address, std::string& subject); + +const Variant& getOption(const std::string& key, const Variant::Map& options) +{ + Variant::Map::const_iterator i = options.find(key); + if (i == options.end()) return EMPTY_VARIANT; + else return i->second; +} + +std::auto_ptr<MessageSource> AddressResolution::resolveSource(qpid::client::Session session, + const Address& address, + const Filter* filter, + const Variant::Map& options) +{ + //TODO: handle case where there exists a queue and an exchange of + //the same name (hence an unqualified address is ambiguous) + + //TODO: make sure specified address type gives sane error message + //if it does npt match the configuration on server + + if (isQueue(session, address)) { + //TODO: support auto-created queue as source, if requested by specific option + + AcceptMode accept = getOption(UNACKNOWLEDGED, options).asBool() ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT; + AcquireMode acquire = getOption(BROWSE, options).asBool() ? ACQUIRE_MODE_NOT_ACQUIRED : ACQUIRE_MODE_PRE_ACQUIRED; + bool exclusive = getOption(EXCLUSIVE, options).asBool(); + FieldTable arguments; + //TODO: extract subscribe arguments from options (e.g. either + //filter out already processed keys and send the rest, or have + //a nested map) + + std::auto_ptr<MessageSource> source = + std::auto_ptr<MessageSource>(new QueueSource(address.value, accept, acquire, exclusive, arguments)); + return source; + } else { + //TODO: extract queue options (e.g. no-local) and subscription options (e.g. less important) + std::auto_ptr<Subscription> bindings = + std::auto_ptr<Subscription>(new Subscription(getOption(NAME, options).asString(), + Subscription::getMode(getOption(MODE, options).asString()))); + + qpid::framing::ExchangeQueryResult result = session.exchangeQuery(address.value); + if (result.getNotFound()) { + throw qpid::framing::NotFoundException(QPID_MSG("Address not known: " << address)); + } else if (result.getType() == "topic") { + if (filter) { + if (filter->type != Filter::WILDCARD) { + throw qpid::framing::NotImplementedException( + QPID_MSG("Filters of type " << filter->type << " not supported by address " << address)); + + } + for (std::vector<std::string>::const_iterator i = filter->patterns.begin(); i != filter->patterns.end(); i++) { + bindings->add(address.value, *i, qpid::framing::FieldTable()); + } + } else { + //default is to receive all messages + bindings->add(address.value, "*", qpid::framing::FieldTable()); + } + } else if (result.getType() == "fanout") { + if (filter) { + throw qpid::framing::NotImplementedException(QPID_MSG("Filters are not supported by address " << address)); + } + bindings->add(address.value, address.value, qpid::framing::FieldTable()); + } else if (result.getType() == "direct") { + //TODO: ???? + } else { + //TODO: xml and headers exchanges + throw qpid::framing::NotImplementedException(QPID_MSG("Address type not recognised for " << address)); + } + std::auto_ptr<MessageSource> source = std::auto_ptr<MessageSource>(bindings.release()); + return source; + } +} + + +std::auto_ptr<MessageSink> AddressResolution::resolveSink(qpid::client::Session session, + const qpid::messaging::Address& address, + const qpid::messaging::Variant::Map& /*options*/) +{ + std::auto_ptr<MessageSink> sink; + if (isQueue(session, address)) { + //TODO: support for auto-created queues as sink + sink = std::auto_ptr<MessageSink>(new QueueSink(address.value)); + } else { + std::string subject; + if (isTopic(session, address, subject)) { + //TODO: support for auto-created exchanges as sink + sink = std::auto_ptr<MessageSink>(new Exchange(address.value, subject)); + } else { + if (address.type.empty()) { + throw qpid::framing::NotFoundException(QPID_MSG("Address not known: " << address)); + } else { + throw qpid::framing::NotImplementedException(QPID_MSG("Address type not recognised: " << address.type)); + } + } + } + return sink; +} + +QueueSource::QueueSource(const std::string& _name, AcceptMode _acceptMode, AcquireMode _acquireMode, bool _exclusive, const FieldTable& _options) : + name(_name), acceptMode(_acceptMode), acquireMode(_acquireMode), exclusive(_exclusive), options(_options) {} + +void QueueSource::subscribe(qpid::client::AsyncSession& session, const std::string& destination) +{ + session.messageSubscribe(arg::queue=name, + arg::destination=destination, + arg::acceptMode=acceptMode, + arg::acquireMode=acquireMode, + arg::exclusive=exclusive, + arg::arguments=options); +} + +void QueueSource::cancel(qpid::client::AsyncSession& session, const std::string& destination) +{ + session.messageCancel(destination); +} + +Subscription::Subscription(const std::string& _name, SubscriptionMode mode, const FieldTable& qOptions, const FieldTable& sOptions) + : name(_name), autoDelete(mode == SIMPLE), durable(mode == DURABLE), + queueOptions(qOptions), subscriptionOptions(sOptions) {} + +void Subscription::add(const std::string& exchange, const std::string& key, const FieldTable& options) +{ + bindings.push_back(Binding(exchange, key, options)); +} + +void Subscription::subscribe(qpid::client::AsyncSession& session, const std::string& destination) +{ + if (name.empty()) { + //TODO: use same scheme as JMS client for subscription queue name generation? + queue = session.getId().getName() + destination; + } else { + queue = name; + } + session.queueDeclare(arg::queue=queue, arg::exclusive=true, + arg::autoDelete=autoDelete, arg::durable=durable, arg::arguments=queueOptions); + for (Bindings::const_iterator i = bindings.begin(); i != bindings.end(); ++i) { + session.exchangeBind(arg::queue=queue, arg::exchange=i->exchange, arg::bindingKey=i->key, arg::arguments=i->options); + } + AcceptMode accept = autoDelete ? ACCEPT_MODE_NONE : ACCEPT_MODE_EXPLICIT; + session.messageSubscribe(arg::queue=queue, arg::destination=destination, + arg::exclusive=true, arg::acceptMode=accept, arg::arguments=subscriptionOptions); +} + +void Subscription::cancel(qpid::client::AsyncSession& session, const std::string& destination) +{ + session.messageCancel(destination); + session.queueDelete(arg::queue=queue); +} + +Subscription::Binding::Binding(const std::string& e, const std::string& k, const FieldTable& o): + exchange(e), key(k), options(o) {} + +Subscription::SubscriptionMode Subscription::getMode(const std::string& s) +{ + if (s.empty() || s == SIMPLE_SUBSCRIPTION) return SIMPLE; + else if (s == RELIABLE_SUBSCRIPTION) return RELIABLE; + else if (s == DURABLE_SUBSCRIPTION) return DURABLE; + else throw Exception(QPID_MSG("Unrecognised subscription mode: " << s)); +} + +void convert(qpid::messaging::Message& from, qpid::client::Message& to); + +Exchange::Exchange(const std::string& _name, const std::string& _defaultSubject, + bool _passive, const std::string& _type, bool _durable, const FieldTable& _options) : + name(_name), defaultSubject(_defaultSubject), passive(_passive), type(_type), durable(_durable), options(_options) {} + +void Exchange::declare(qpid::client::AsyncSession& session, const std::string&) +{ + //TODO: should this really by synchronous? want to get error if not valid... + if (passive) { + sync(session).exchangeDeclare(arg::exchange=name, arg::passive=true); + } else { + sync(session).exchangeDeclare(arg::exchange=name, arg::type=type, arg::durable=durable, arg::arguments=options); + } +} + +void Exchange::send(qpid::client::AsyncSession& session, const std::string&, qpid::messaging::Message& m) +{ + qpid::client::Message message; + convert(m, message); + if (message.getDeliveryProperties().getRoutingKey().empty() && !defaultSubject.empty()) { + message.getDeliveryProperties().setRoutingKey(defaultSubject); + } + session.messageTransfer(arg::destination=name, arg::content=message); +} + +void Exchange::cancel(qpid::client::AsyncSession&, const std::string&) {} + +QueueSink::QueueSink(const std::string& _name, bool _passive, bool _exclusive, + bool _autoDelete, bool _durable, const FieldTable& _options) : + name(_name), passive(_passive), exclusive(_exclusive), + autoDelete(_autoDelete), durable(_durable), options(_options) {} + +void QueueSink::declare(qpid::client::AsyncSession& session, const std::string&) +{ + //TODO: should this really by synchronous? + if (passive) { + sync(session).queueDeclare(arg::queue=name, arg::passive=true); + } else { + sync(session).queueDeclare(arg::queue=name, arg::exclusive=exclusive, arg::durable=durable, + arg::autoDelete=autoDelete, arg::arguments=options); + } +} +void QueueSink::send(qpid::client::AsyncSession& session, const std::string&, qpid::messaging::Message& m) +{ + qpid::client::Message message; + convert(m, message); + message.getDeliveryProperties().setRoutingKey(name); + session.messageTransfer(arg::content=message); +} + +void QueueSink::cancel(qpid::client::AsyncSession&, const std::string&) {} + +template <class T> void encode(qpid::messaging::Message& from) +{ + T codec; + from.encode(codec); + from.setContentType(T::contentType); +} + +void translate(const Variant::Map& from, FieldTable& to);//implementation in Codecs.cpp + +void convert(qpid::messaging::Message& from, qpid::client::Message& to) +{ + //TODO: need to avoid copying as much as possible + if (from.getContent().isList()) encode<ListCodec>(from); + if (from.getContent().isMap()) encode<MapCodec>(from); + to.setData(from.getBytes()); + to.getDeliveryProperties().setRoutingKey(from.getSubject()); + //TODO: set other delivery properties + to.getMessageProperties().setContentType(from.getContentType()); + const Address& address = from.getReplyTo(); + if (!address.value.empty()) { + to.getMessageProperties().setReplyTo(AddressResolution::convert(address)); + } + translate(from.getHeaders(), to.getMessageProperties().getApplicationHeaders()); + //TODO: set other message properties +} + +Address AddressResolution::convert(const qpid::framing::ReplyTo& rt) +{ + if (rt.getExchange().empty()) { + if (rt.getRoutingKey().empty()) { + return Address();//empty address + } else { + return Address(rt.getRoutingKey(), QUEUE_ADDRESS); + } + } else { + if (rt.getRoutingKey().empty()) { + return Address(rt.getExchange(), TOPIC_ADDRESS); + } else { + return Address(rt.getExchange() + DIVIDER + rt.getRoutingKey(), TOPIC_ADDRESS_AND_SUBJECT); + } + } +} + +qpid::framing::ReplyTo AddressResolution::convert(const Address& address) +{ + if (address.type == QUEUE_ADDRESS || address.type.empty()) { + return ReplyTo(EMPTY_STRING, address.value); + } else if (address.type == TOPIC_ADDRESS) { + return ReplyTo(address.value, EMPTY_STRING); + } else if (address.type == TOPIC_ADDRESS_AND_SUBJECT) { + //need to split the value + string::size_type i = address.value.find(DIVIDER); + if (i != string::npos) { + std::string exchange = address.value.substr(0, i); + std::string routingKey; + if (i+1 < address.value.size()) { + routingKey = address.value.substr(i+1); + } + return ReplyTo(exchange, routingKey); + } else { + return ReplyTo(address.value, EMPTY_STRING); + } + } else { + QPID_LOG(notice, "Unrecognised type for reply-to: " << address.type); + //treat as queue + return ReplyTo(EMPTY_STRING, address.value); + } +} + +bool isQueue(qpid::client::Session session, const qpid::messaging::Address& address) +{ + return address.type == QUEUE_ADDRESS || + (address.type.empty() && session.queueQuery(address.value).getQueue() == address.value); +} + +bool isTopic(qpid::client::Session session, const qpid::messaging::Address& address, std::string& subject) +{ + if (address.type.empty()) { + return !session.exchangeQuery(address.value).getNotFound(); + } else if (address.type == TOPIC_ADDRESS) { + return true; + } else if (address.type == TOPIC_ADDRESS_AND_SUBJECT) { + string::size_type i = address.value.find(DIVIDER); + if (i != string::npos) { + std::string exchange = address.value.substr(0, i); + if (i+1 < address.value.size()) { + subject = address.value.substr(i+1); + } + } + return true; + } else { + return false; + } +} + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h new file mode 100644 index 0000000000..87758abe6d --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.h @@ -0,0 +1,68 @@ +#ifndef QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H +#define QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Variant.h" +#include "qpid/client/Session.h" + +namespace qpid { + +namespace framing{ +class ReplyTo; +} + +namespace messaging { +class Address; +class Filter; +} + +namespace client { +namespace amqp0_10 { + +class MessageSource; +class MessageSink; + +/** + * Maps from a generic Address and optional Filter to an AMQP 0-10 + * MessageSource which will then be used by a ReceiverImpl instance + * created for the address. + */ +class AddressResolution +{ + public: + std::auto_ptr<MessageSource> resolveSource(qpid::client::Session session, + const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::Variant::Map& options); + + std::auto_ptr<MessageSink> resolveSink(qpid::client::Session session, + const qpid::messaging::Address& address, + const qpid::messaging::Variant::Map& options); + + static qpid::messaging::Address convert(const qpid::framing::ReplyTo&); + static qpid::framing::ReplyTo convert(const qpid::messaging::Address&); + + private: +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_ADDRESSRESOLUTION_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp new file mode 100644 index 0000000000..9aee3118fe --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/Codecs.cpp @@ -0,0 +1,299 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/messaging/Variant.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/List.h" +#include <algorithm> +#include <functional> + +using namespace qpid::framing; +using namespace qpid::messaging; + +namespace qpid { +namespace client { +namespace amqp0_10 { + +namespace { +const std::string iso885915("iso-8859-15"); +const std::string utf8("utf8"); +const std::string utf16("utf16"); +const std::string amqp0_10_binary("amqp0-10:binary"); +const std::string amqp0_10_bit("amqp0-10:bit"); +const std::string amqp0_10_datetime("amqp0-10:datetime"); +const std::string amqp0_10_struct("amqp0-10:struct"); +} + +template <class T, class U, class F> void convert(const T& from, U& to, F f) +{ + std::transform(from.begin(), from.end(), std::inserter(to, to.begin()), f); +} + +Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in); +FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in); +Variant toVariant(boost::shared_ptr<FieldValue> in); +boost::shared_ptr<FieldValue> toFieldValue(const Variant& in); + +template <class T, class U, class F> void translate(boost::shared_ptr<FieldValue> in, U& u, F f) +{ + T t; + getEncodedValue<T>(in, t); + convert(t, u, f); +} + +template <class T, class U, class F> T* toFieldValueCollection(const U& u, F f) +{ + typename T::ValueType t; + convert(u, t, f); + return new T(t); +} + +FieldTableValue* toFieldTableValue(const Variant::Map& map) +{ + FieldTable ft; + convert(map, ft, &toFieldTableEntry); + return new FieldTableValue(ft); +} + +ListValue* toListValue(const Variant::List& list) +{ + List l; + convert(list, l, &toFieldValue); + return new ListValue(l); +} + +void setEncodingFor(Variant& out, uint8_t code) +{ + switch(code){ + case 0x80: + case 0x90: + case 0xa0: + out.setEncoding(amqp0_10_binary); + break; + case 0x84: + case 0x94: + out.setEncoding(iso885915); + break; + case 0x85: + case 0x95: + out.setEncoding(utf8); + break; + case 0x86: + case 0x96: + out.setEncoding(utf16); + break; + case 0xab: + out.setEncoding(amqp0_10_struct); + break; + default: + //do nothing + break; + } +} + +Variant toVariant(boost::shared_ptr<FieldValue> in) +{ + Variant out; + //based on AMQP 0-10 typecode, pick most appropriate variant type + switch (in->getType()) { + //Fixed Width types: + case 0x01: out.setEncoding(amqp0_10_binary); + case 0x02: out = in->getIntegerValue<int8_t, 1>(); break; + case 0x03: out = in->getIntegerValue<uint8_t, 1>(); break; + case 0x04: break; //TODO: iso-8859-15 char + case 0x08: out = in->getIntegerValue<bool, 1>(); break; + case 0x010: out.setEncoding(amqp0_10_binary); + case 0x011: out = in->getIntegerValue<int16_t, 2>(); break; + case 0x012: out = in->getIntegerValue<uint16_t, 2>(); break; + case 0x020: out.setEncoding(amqp0_10_binary); + case 0x021: out = in->getIntegerValue<int32_t, 4>(); break; + case 0x022: out = in->getIntegerValue<uint32_t, 4>(); break; + case 0x023: out = in->get<float>(); break; + case 0x027: break; //TODO: utf-32 char + case 0x030: out.setEncoding(amqp0_10_binary); + case 0x031: out = in->getIntegerValue<int64_t, 8>(); break; + case 0x038: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding + case 0x032: out = in->getIntegerValue<uint64_t, 8>(); break; + case 0x033:out = in->get<double>(); break; + + //TODO: figure out whether and how to map values with codes 0x40-0xd8 + + case 0xf0: break;//void, which is the default value for Variant + case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant + + //Variable Width types: + //strings: + case 0x80: + case 0x84: + case 0x85: + case 0x86: + case 0x90: + case 0x94: + case 0x95: + case 0x96: + case 0xa0: + case 0xab: + setEncodingFor(out, in->getType()); + out = in->get<std::string>(); + break; + + case 0xa8: + out = Variant::Map(); + translate<FieldTable>(in, out.asMap(), &toVariantMapEntry); + break; + + case 0xa9: + out = Variant::List(); + translate<List>(in, out.asList(), &toVariant); + break; + case 0xaa: //convert amqp0-10 array into variant list + out = Variant::List(); + translate<Array>(in, out.asList(), &toVariant); + break; + + default: + //error? + break; + } + return out; +} + +boost::shared_ptr<FieldValue> toFieldValue(const Variant& in) +{ + boost::shared_ptr<FieldValue> out; + switch (in.getType()) { + case VOID: out = boost::shared_ptr<FieldValue>(new VoidValue()); break; + case BOOL: out = boost::shared_ptr<FieldValue>(new BoolValue(in.asBool())); break; + case UINT8: out = boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8())); break; + case UINT16: out = boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16())); break; + case UINT32: out = boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32())); break; + case UINT64: out = boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64())); break; + case INT8: out = boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8())); break; + case INT16: out = boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16())); break; + case INT32: out = boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32())); break; + case INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break; + case FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break; + case DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break; + //TODO: check encoding (and length?) when deciding what AMQP type to treat string as + case STRING: out = boost::shared_ptr<FieldValue>(new Str16Value(in.asString())); break; + case MAP: + //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<FieldTableValue>(in.asMap(), &toFieldTableEntry)); + out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap())); + break; + case LIST: + //out = boost::shared_ptr<FieldValue>(toFieldValueCollection<ListValue>(in.asList(), &toFieldValue)); + out = boost::shared_ptr<FieldValue>(toListValue(in.asList())); + break; + } + return out; +} + +Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in) +{ + return Variant::Map::value_type(in.first, toVariant(in.second)); +} + +FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in) +{ + return FieldTable::value_type(in.first, toFieldValue(in.second)); +} + +struct EncodeBuffer +{ + char* data; + Buffer buffer; + + EncodeBuffer(size_t size) : data(new char[size]), buffer(data, size) {} + ~EncodeBuffer() { delete[] data; } + + template <class T> void encode(T& t) { t.encode(buffer); } + + void getData(std::string& s) { + s.assign(data, buffer.getSize()); + } +}; + +struct DecodeBuffer +{ + Buffer buffer; + + DecodeBuffer(const std::string& s) : buffer(const_cast<char*>(s.data()), s.size()) {} + + template <class T> void decode(T& t) { t.decode(buffer); } + +}; + +template <class T, class U, class F> void _encode(const U& value, std::string& data, F f) +{ + T t; + convert(value, t, f); + EncodeBuffer buffer(t.encodedSize()); + buffer.encode(t); + buffer.getData(data); +} + +template <class T, class U, class F> void _decode(const std::string& data, U& value, F f) +{ + T t; + DecodeBuffer buffer(data); + buffer.decode(t); + convert(t, value, f); +} + +void MapCodec::encode(const Variant& value, std::string& data) +{ + _encode<FieldTable>(value.asMap(), data, &toFieldTableEntry); +} + +void MapCodec::decode(const std::string& data, Variant& value) +{ + value = Variant::Map(); + _decode<FieldTable>(data, value.asMap(), &toVariantMapEntry); +} + +void ListCodec::encode(const Variant& value, std::string& data) +{ + _encode<List>(value.asList(), data, &toFieldValue); +} + +void ListCodec::decode(const std::string& data, Variant& value) +{ + value = Variant::List(); + _decode<List>(data, value.asList(), &toVariant); +} + +void translate(const Variant::Map& from, FieldTable& to) +{ + convert(from, to, &toFieldTableEntry); +} + +void translate(const FieldTable& from, Variant::Map& to) +{ + convert(from, to, &toVariantMapEntry); +} + +const std::string ListCodec::contentType("amqp0_10/list"); +const std::string MapCodec::contentType("amqp0_10/map"); + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.cpp new file mode 100644 index 0000000000..52b623b65c --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.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 "CompletionTracker.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::framing::SequenceNumber; + +void CompletionTracker::track(SequenceNumber command, void* token) +{ + tokens[command] = token; +} + +void CompletionTracker::completedTo(SequenceNumber command) +{ + Tokens::iterator i = tokens.lower_bound(command); + if (i != tokens.end()) { + lastCompleted = i->second; + tokens.erase(tokens.begin(), ++i); + } +} + +void* CompletionTracker::getLastCompletedToken() +{ + return lastCompleted; +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h new file mode 100644 index 0000000000..6147c5682e --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/CompletionTracker.h @@ -0,0 +1,50 @@ +#ifndef QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H +#define QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/framing/SequenceNumber.h" +#include <map> + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Provides a mapping from command ids to application supplied + * 'tokens', and is used to determine when the sending or + * acknowledging of a specific message is complete. + */ +class CompletionTracker +{ + public: + void track(qpid::framing::SequenceNumber command, void* token); + void completedTo(qpid::framing::SequenceNumber command); + void* getLastCompletedToken(); + private: + typedef std::map<qpid::framing::SequenceNumber, void*> Tokens; + Tokens tokens; + void* lastCompleted; +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_COMPLETIONTRACKER_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp new file mode 100644 index 0000000000..9f738731e2 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -0,0 +1,79 @@ +/* + * + * 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 "ConnectionImpl.h" +#include "SessionImpl.h" +#include "qpid/messaging/Session.h" +#include "qpid/client/ConnectionSettings.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::messaging::Variant; + +template <class T> void setIfFound(const Variant::Map& map, const std::string& key, T& value) +{ + Variant::Map::const_iterator i = map.find(key); + if (i != map.end()) { + value = (T) i->second; + } +} + +void convert(const Variant::Map& from, ConnectionSettings& to) +{ + setIfFound(from, "username", to.username); + setIfFound(from, "password", to.password); + setIfFound(from, "sasl-mechanism", to.mechanism); + setIfFound(from, "sasl-service", to.service); + setIfFound(from, "sasl-min-ssf", to.minSsf); + setIfFound(from, "sasl-max-ssf", to.maxSsf); + + setIfFound(from, "heartbeat", to.heartbeat); + setIfFound(from, "tcp-nodelay", to.tcpNoDelay); + + setIfFound(from, "locale", to.locale); + setIfFound(from, "max-channels", to.maxChannels); + setIfFound(from, "max-frame-size", to.maxFrameSize); + setIfFound(from, "bounds", to.bounds); +} + +ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& options) +{ + QPID_LOG(debug, "Opening connection to " << url << " with " << options); + Url u(url); + ConnectionSettings settings; + convert(options, settings); + connection.open(u, settings); +} + +void ConnectionImpl::close() +{ + connection.close(); +} + +qpid::messaging::Session ConnectionImpl::newSession() +{ + qpid::messaging::Session impl(new SessionImpl(connection.newSession())); + return impl; +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h new file mode 100644 index 0000000000..120a8ab9d8 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h @@ -0,0 +1,43 @@ +#ifndef QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H +#define QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/ConnectionImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/client/Connection.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +class ConnectionImpl : public qpid::messaging::ConnectionImpl +{ + public: + ConnectionImpl(const std::string& url, const qpid::messaging::Variant::Map& options); + void close(); + qpid::messaging::Session newSession(); + private: + qpid::client::Connection connection; +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_CONNECTIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp new file mode 100644 index 0000000000..83e1b48bed --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp @@ -0,0 +1,241 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/IncomingMessages.h" +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/Codecs.h" +#include "qpid/client/SessionImpl.h" +#include "qpid/client/SessionBase_0_10Access.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Variant.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/enum.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using namespace qpid::framing; +using namespace qpid::framing::message; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::messaging::Variant; + +namespace { +const std::string EMPTY_STRING; + + +struct GetNone : IncomingMessages::Handler +{ + bool accept(IncomingMessages::MessageTransfer&) { return false; } +}; + +struct GetAny : IncomingMessages::Handler +{ + bool accept(IncomingMessages::MessageTransfer& transfer) + { + transfer.retrieve(0); + return true; + } +}; + +struct MatchAndTrack +{ + const std::string destination; + SequenceSet ids; + + MatchAndTrack(const std::string& d) : destination(d) {} + + bool operator()(boost::shared_ptr<qpid::framing::FrameSet> command) + { + if (command->as<MessageTransferBody>()->getDestination() == destination) { + ids.add(command->getId()); + return true; + } else { + return false; + } + } +}; +} + +IncomingMessages::IncomingMessages(qpid::client::AsyncSession s) : + session(s), + incoming(SessionBase_0_10Access(session).get()->getDemux().getDefault()) {} + +bool IncomingMessages::get(Handler& handler, Duration timeout) +{ + //search through received list for any transfer of interest: + for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i++) + { + MessageTransfer transfer(*i, *this); + if (handler.accept(transfer)) { + received.erase(i); + return true; + } + } + //none found, check incoming: + return process(&handler, timeout); +} + +void IncomingMessages::accept() +{ + session.messageAccept(unaccepted); + unaccepted.clear(); +} + +void IncomingMessages::releaseAll() +{ + //first process any received messages... + while (!received.empty()) { + retrieve(received.front(), 0); + received.pop_front(); + } + //then pump out any available messages from incoming queue... + GetAny handler; + while (process(&handler, 0)); + //now release all messages + session.messageRelease(unaccepted); + unaccepted.clear(); +} + +void IncomingMessages::releasePending(const std::string& destination) +{ + //first pump all available messages from incoming to received... + while (process(0, 0)); + + //now remove all messages for this destination from received list, recording their ids... + MatchAndTrack match(destination); + for (FrameSetQueue::iterator i = received.begin(); i != received.end(); i = match(*i) ? received.erase(i) : ++i); + //now release those messages + session.messageRelease(match.ids); +} + +/** + * Get a frameset from session queue, waiting for up to the specified + * duration and returning true if this could be achieved, false + * otherwise. If a destination is supplied, only return a message for + * that destination. In this case messages from other destinations + * will be held on a received queue. + */ +bool IncomingMessages::process(Handler* handler, qpid::sys::Duration duration) +{ + AbsTime deadline(AbsTime::now(), duration); + FrameSet::shared_ptr content; + for (Duration timeout = duration; incoming->pop(content, timeout); timeout = Duration(AbsTime::now(), deadline)) { + if (content->isA<MessageTransferBody>()) { + MessageTransfer transfer(content, *this); + if (handler && handler->accept(transfer)) { + QPID_LOG(debug, "Delivered " << *content->getMethod()); + return true; + } else { + //received message for another destination, keep for later + QPID_LOG(debug, "Pushed " << *content->getMethod() << " to received queue"); + received.push_back(content); + } + } else { + //TODO: handle other types of commands (e.g. message-accept, message-flow etc) + } + } + return false; +} + +void populate(qpid::messaging::Message& message, FrameSet& command); + +/** + * Called when message is retrieved; records retrieval for subsequent + * acceptance, marks the command as completed and converts command to + * message if message is required + */ +void IncomingMessages::retrieve(FrameSetPtr command, qpid::messaging::Message* message) +{ + if (message) { + populate(*message, *command); + } + const MessageTransferBody* transfer = command->as<MessageTransferBody>(); + if (transfer->getAcquireMode() == ACQUIRE_MODE_PRE_ACQUIRED && transfer->getAcceptMode() == ACCEPT_MODE_EXPLICIT) { + unaccepted.add(command->getId()); + } + session.markCompleted(command->getId(), false, false); +} + +IncomingMessages::MessageTransfer::MessageTransfer(FrameSetPtr c, IncomingMessages& p) : content(c), parent(p) {} + +const std::string& IncomingMessages::MessageTransfer::getDestination() +{ + return content->as<MessageTransferBody>()->getDestination(); +} +void IncomingMessages::MessageTransfer::retrieve(qpid::messaging::Message* message) +{ + parent.retrieve(content, message); +} + +void translate(const FieldTable& from, Variant::Map& to);//implemented in Codecs.cpp + +void populateHeaders(qpid::messaging::Message& message, + const DeliveryProperties* deliveryProperties, + const MessageProperties* messageProperties) +{ + if (deliveryProperties) { + message.setSubject(deliveryProperties->getRoutingKey()); + //TODO: convert other delivery properties + } + if (messageProperties) { + message.setContentType(messageProperties->getContentType()); + if (messageProperties->hasReplyTo()) { + message.setReplyTo(AddressResolution::convert(messageProperties->getReplyTo())); + } + translate(messageProperties->getApplicationHeaders(), message.getHeaders()); + //TODO: convert other message properties + } +} + +void populateHeaders(qpid::messaging::Message& message, const AMQHeaderBody* headers) +{ + populateHeaders(message, headers->get<DeliveryProperties>(), headers->get<MessageProperties>()); +} + +void populate(qpid::messaging::Message& message, FrameSet& command) +{ + //need to be able to link the message back to the transfer it was delivered by + //e.g. for rejecting. TODO: hide this from API + uint32_t commandId = command.getId(); + message.setInternalId(reinterpret_cast<void*>(commandId)); + + command.getContent(message.getBytes()); + + populateHeaders(message, command.getHeaders()); + + //decode content if necessary + if (message.getContentType() == ListCodec::contentType) { + ListCodec codec; + message.decode(codec); + } else if (message.getContentType() == MapCodec::contentType) { + MapCodec codec; + message.decode(codec); + } +} + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h new file mode 100644 index 0000000000..c4346fd7d7 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/IncomingMessages.h @@ -0,0 +1,91 @@ +#ifndef QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H +#define QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_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 "qpid/client/AsyncSession.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/sys/BlockingQueue.h" +#include "qpid/sys/Time.h" + +namespace qpid { + +namespace framing{ +class FrameSet; +} + +namespace messaging { +class Message; +} + +namespace client { +namespace amqp0_10 { + +/** + * + */ +class IncomingMessages +{ + public: + typedef boost::shared_ptr<qpid::framing::FrameSet> FrameSetPtr; + class MessageTransfer + { + public: + const std::string& getDestination(); + void retrieve(qpid::messaging::Message* message); + private: + FrameSetPtr content; + IncomingMessages& parent; + + MessageTransfer(FrameSetPtr, IncomingMessages&); + friend class IncomingMessages; + }; + + struct Handler + { + virtual ~Handler() {} + virtual bool accept(MessageTransfer& transfer) = 0; + }; + + IncomingMessages(qpid::client::AsyncSession session); + bool get(Handler& handler, qpid::sys::Duration timeout); + //bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout); + //bool get(const std::string& destination, qpid::messaging::Message& message, qpid::sys::Duration timeout); + void accept(); + void releaseAll(); + void releasePending(const std::string& destination); + private: + typedef std::deque<FrameSetPtr> FrameSetQueue; + + qpid::client::AsyncSession session; + qpid::framing::SequenceSet unaccepted; + boost::shared_ptr< sys::BlockingQueue<FrameSetPtr> > incoming; + FrameSetQueue received; + + bool process(Handler*, qpid::sys::Duration); + void retrieve(FrameSetPtr, qpid::messaging::Message*); + +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_INCOMINGMESSAGES_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h new file mode 100644 index 0000000000..19d5e4ef82 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSink.h @@ -0,0 +1,50 @@ +#ifndef QPID_CLIENT_AMQP0_10_MESSAGESINK_H +#define QPID_CLIENT_AMQP0_10_MESSAGESINK_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 "qpid/client/AsyncSession.h" + +namespace qpid { + +namespace messaging { +class Message; +} + +namespace client { +namespace amqp0_10 { + +/** + * + */ +class MessageSink +{ + public: + virtual ~MessageSink() {} + virtual void declare(qpid::client::AsyncSession& session, const std::string& name) = 0; + virtual void send(qpid::client::AsyncSession& session, const std::string& name, qpid::messaging::Message& message) = 0; + virtual void cancel(qpid::client::AsyncSession& session, const std::string& name) = 0; + private: +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESINK_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h new file mode 100644 index 0000000000..74f2732f59 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/MessageSource.h @@ -0,0 +1,47 @@ +#ifndef QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H +#define QPID_CLIENT_AMQP0_10_MESSAGESOURCE_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 "qpid/client/AsyncSession.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +/** + * Abstraction behind which the AMQP 0-10 commands required to + * establish (and tear down) an incoming stream of messages from a + * given address are hidden. + */ +class MessageSource +{ + public: + virtual ~MessageSource() {} + virtual void subscribe(qpid::client::AsyncSession& session, const std::string& destination) = 0; + virtual void cancel(qpid::client::AsyncSession& session, const std::string& destination) = 0; + + private: +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_MESSAGESOURCE_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp new file mode 100644 index 0000000000..e6ed4bfc4e --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.cpp @@ -0,0 +1,146 @@ +/* + * + * 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 "ReceiverImpl.h" +#include "MessageSource.h" +#include "SessionImpl.h" +#include "qpid/messaging/MessageListener.h" +#include "qpid/messaging/Receiver.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +using qpid::messaging::Receiver; + +void ReceiverImpl::received(qpid::messaging::Message&) +{ + //TODO: should this be configurable + if (capacity && --window <= capacity/2) { + session.sendCompletion(); + window = capacity; + } +} + +bool ReceiverImpl::get(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + return parent.get(*this, message, timeout); +} + +qpid::messaging::Message ReceiverImpl::get(qpid::sys::Duration timeout) +{ + qpid::messaging::Message result; + if (!get(result, timeout)) throw Receiver::NoMessageAvailable(); + return result; +} + +bool ReceiverImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + if (capacity == 0 && !cancelled) { + session.messageFlow(destination, CREDIT_UNIT_MESSAGE, 1); + if (!started) session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit); + } + + if (get(message, timeout)) { + return true; + } else { + if (!cancelled) { + sync(session).messageFlush(destination); + start();//reallocate credit + } + return get(message, 0); + } +} + +qpid::messaging::Message ReceiverImpl::fetch(qpid::sys::Duration timeout) +{ + qpid::messaging::Message result; + if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable(); + return result; +} + +void ReceiverImpl::cancel() +{ + if (!cancelled) { + //TODO: should syncronicity be an optional argument to this call? + source->cancel(session, destination); + //need to be sure cancel is complete and all incoming + //framesets are processed before removing the receiver + parent.receiverCancelled(destination); + cancelled = true; + } +} + +void ReceiverImpl::start() +{ + if (!cancelled) { + started = true; + session.messageSetFlowMode(destination, capacity > 0); + session.messageFlow(destination, CREDIT_UNIT_MESSAGE, capacity); + session.messageFlow(destination, CREDIT_UNIT_BYTE, byteCredit); + window = capacity; + } +} + +void ReceiverImpl::stop() +{ + session.messageStop(destination); + started = false; +} + +void ReceiverImpl::subscribe() +{ + source->subscribe(session, destination); +} + +void ReceiverImpl::setSession(qpid::client::AsyncSession s) +{ + session = s; + if (!cancelled) { + subscribe(); + //if we were in started state before the session was changed, + //start again on this new session + //TODO: locking if receiver is to be threadsafe... + if (started) start(); + } +} + +void ReceiverImpl::setCapacity(uint32_t c) +{ + if (c != capacity) { + capacity = c; + if (!cancelled && started) { + stop(); + start(); + } + } +} + +void ReceiverImpl::setListener(qpid::messaging::MessageListener* l) { listener = l; } +qpid::messaging::MessageListener* ReceiverImpl::getListener() { return listener; } + +const std::string& ReceiverImpl::getName() const { return destination; } + +ReceiverImpl::ReceiverImpl(SessionImpl& p, const std::string& name, std::auto_ptr<MessageSource> s) : + parent(p), source(s), destination(name), byteCredit(0xFFFFFFFF), + capacity(0), started(false), cancelled(false), listener(0), window(0) {} + + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h new file mode 100644 index 0000000000..b549242d35 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h @@ -0,0 +1,76 @@ +#ifndef QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H +#define QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Message.h" +#include "qpid/messaging/ReceiverImpl.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/sys/Time.h" +#include <memory> + +namespace qpid { +namespace client { +namespace amqp0_10 { + +class MessageSource; +class SessionImpl; + +/** + * A receiver implementation based on an AMQP 0-10 subscription. + */ +class ReceiverImpl : public qpid::messaging::ReceiverImpl +{ + public: + + ReceiverImpl(SessionImpl& parent, const std::string& name, std::auto_ptr<MessageSource> source); + + bool get(qpid::messaging::Message& message, qpid::sys::Duration timeout); + qpid::messaging::Message get(qpid::sys::Duration timeout); + bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout); + qpid::messaging::Message fetch(qpid::sys::Duration timeout); + void cancel(); + void start(); + void stop(); + void subscribe(); + void setSession(qpid::client::AsyncSession s); + const std::string& getName() const; + void setCapacity(uint32_t); + void setListener(qpid::messaging::MessageListener* listener); + qpid::messaging::MessageListener* getListener(); + void received(qpid::messaging::Message& message); + private: + SessionImpl& parent; + const std::auto_ptr<MessageSource> source; + const std::string destination; + const uint32_t byteCredit; + + uint32_t capacity; + qpid::client::AsyncSession session; + bool started; + bool cancelled; + qpid::messaging::MessageListener* listener; + uint32_t window; +}; + +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_RECEIVERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp new file mode 100644 index 0000000000..ac36eb1537 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp @@ -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 "SenderImpl.h" +#include "MessageSink.h" +#include "SessionImpl.h" + +namespace qpid { +namespace client { +namespace amqp0_10 { + +SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name, std::auto_ptr<MessageSink> _sink) : + parent(_parent), name(_name), sink(_sink) {} + +void SenderImpl::send(qpid::messaging::Message& m) +{ + sink->send(session, name, m); +} + +void SenderImpl::cancel() +{ + sink->cancel(session, name); + parent.senderCancelled(name); +} + +void SenderImpl::setSession(qpid::client::AsyncSession s) +{ + session = s; + sink->declare(session, name); +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h new file mode 100644 index 0000000000..e737450ba1 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.h @@ -0,0 +1,58 @@ +#ifndef QPID_CLIENT_AMQP0_10_SENDERIMPL_H +#define QPID_CLIENT_AMQP0_10_SENDERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Message.h" +#include "qpid/messaging/SenderImpl.h" +#include "qpid/client/AsyncSession.h" +#include <memory> + +namespace qpid { +namespace client { +namespace amqp0_10 { + +class MessageSink; +class SessionImpl; + +/** + * + */ +class SenderImpl : public qpid::messaging::SenderImpl +{ + public: + SenderImpl(SessionImpl& parent, const std::string& name, std::auto_ptr<MessageSink> sink); + void send(qpid::messaging::Message&); + void cancel(); + void setSession(qpid::client::AsyncSession); + + private: + SessionImpl& parent; + const std::string name; + std::auto_ptr<MessageSink> sink; + + qpid::client::AsyncSession session; + std::string destination; + std::string routingKey; +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_SENDERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp new file mode 100644 index 0000000000..647ace5f92 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp @@ -0,0 +1,281 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/amqp0_10/SessionImpl.h" +#include "qpid/client/amqp0_10/ReceiverImpl.h" +#include "qpid/client/amqp0_10/SenderImpl.h" +#include "qpid/client/amqp0_10/MessageSource.h" +#include "qpid/client/amqp0_10/MessageSink.h" +#include "qpid/client/PrivateImplRef.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageListener.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Session.h" +#include "qpid/framing/reply_exceptions.h" +#include <boost/format.hpp> +#include <boost/function.hpp> +#include <boost/intrusive_ptr.hpp> + +using qpid::messaging::Filter; +using qpid::messaging::Sender; +using qpid::messaging::Receiver; +using qpid::messaging::VariantMap; + +namespace qpid { +namespace client { +namespace amqp0_10 { + +SessionImpl::SessionImpl(qpid::client::Session s) : session(s), incoming(session) {} + + +void SessionImpl::commit() +{ + qpid::sys::Mutex::ScopedLock l(lock); + incoming.accept(); + session.txCommit(); +} + +void SessionImpl::rollback() +{ + qpid::sys::Mutex::ScopedLock l(lock); + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) i->second.stop(); + //ensure that stop has been processed and all previously sent + //messages are available for release: + session.sync(); + incoming.releaseAll(); + session.txRollback(); + for (Receivers::iterator i = receivers.begin(); i != receivers.end(); ++i) i->second.start(); +} + +void SessionImpl::acknowledge() +{ + qpid::sys::Mutex::ScopedLock l(lock); + incoming.accept(); +} + +void SessionImpl::reject(qpid::messaging::Message& m) +{ + qpid::sys::Mutex::ScopedLock l(lock); + //TODO: how do I get the id of the original transfer command? think this through some more... + SequenceNumber id(reinterpret_cast<uint32_t>(m.getInternalId())); + SequenceSet set; + set.add(id); + session.messageReject(set); +} + +void SessionImpl::close() +{ + session.close(); +} + +void translate(const VariantMap& options, SubscriptionSettings& settings) +{ + //TODO: fill this out + VariantMap::const_iterator i = options.find("auto_acknowledge"); + if (i != options.end()) { + settings.autoAck = i->second.asInt32(); + } +} + +template <class T, class S> boost::intrusive_ptr<S> getImplPtr(T& t) +{ + return boost::dynamic_pointer_cast<S>(qpid::client::PrivateImplRef<T>::get(t)); +} + +template <class T> void getFreeKey(std::string& key, T& map) +{ + std::string name = key; + int count = 1; + for (typename T::const_iterator i = map.find(name); i != map.end(); i = map.find(name)) { + name = (boost::format("%1%_%2%") % key % ++count).str(); + } + key = name; +} + +Sender SessionImpl::createSender(const qpid::messaging::Address& address, const VariantMap& options) +{ + qpid::sys::Mutex::ScopedLock l(lock); + std::auto_ptr<MessageSink> sink = resolver.resolveSink(session, address, options); + std::string name = address; + getFreeKey(name, senders); + Sender sender(new SenderImpl(*this, name, sink)); + getImplPtr<Sender, SenderImpl>(sender)->setSession(session); + senders[name] = sender; + return sender; +} +Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const VariantMap& options) +{ + return addReceiver(address, 0, options); +} +Receiver SessionImpl::createReceiver(const qpid::messaging::Address& address, const Filter& filter, const VariantMap& options) +{ + return addReceiver(address, &filter, options); +} + +Receiver SessionImpl::addReceiver(const qpid::messaging::Address& address, const Filter* filter, const VariantMap& options) +{ + qpid::sys::Mutex::ScopedLock l(lock); + std::auto_ptr<MessageSource> source = resolver.resolveSource(session, address, filter, options); + std::string name = address; + getFreeKey(name, receivers); + Receiver receiver(new ReceiverImpl(*this, name, source)); + getImplPtr<Receiver, ReceiverImpl>(receiver)->setSession(session); + receivers[name] = receiver; + return receiver; +} + +qpid::messaging::Address SessionImpl::createTempQueue(const std::string& baseName) +{ + std::string name = baseName + std::string("_") + session.getId().getName(); + session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true); + return qpid::messaging::Address(name); +} + +SessionImpl& SessionImpl::convert(qpid::messaging::Session& s) +{ + boost::intrusive_ptr<SessionImpl> impl = getImplPtr<qpid::messaging::Session, SessionImpl>(s); + if (!impl) { + throw qpid::Exception(QPID_MSG("Configuration error; require qpid::client::amqp0_10::SessionImpl")); + } + return *impl; +} + +namespace { + +struct IncomingMessageHandler : IncomingMessages::Handler +{ + typedef boost::function1<bool, IncomingMessages::MessageTransfer&> Callback; + Callback callback; + + IncomingMessageHandler(Callback c) : callback(c) {} + + bool accept(IncomingMessages::MessageTransfer& transfer) + { + return callback(transfer); + } +}; + +} + +bool SessionImpl::accept(ReceiverImpl* receiver, + qpid::messaging::Message* message, + bool isDispatch, + IncomingMessages::MessageTransfer& transfer) +{ + if (receiver->getName() == transfer.getDestination()) { + transfer.retrieve(message); + if (isDispatch) { + qpid::sys::Mutex::ScopedUnlock u(lock); + qpid::messaging::MessageListener* listener = receiver->getListener(); + if (listener) listener->received(*message); + } + receiver->received(*message); + return true; + } else { + return false; + } +} + +bool SessionImpl::acceptAny(qpid::messaging::Message* message, bool isDispatch, IncomingMessages::MessageTransfer& transfer) +{ + Receivers::iterator i = receivers.find(transfer.getDestination()); + if (i == receivers.end()) { + QPID_LOG(error, "Received message for unknown destination " << transfer.getDestination()); + return false; + } else { + boost::intrusive_ptr<ReceiverImpl> receiver = getImplPtr<Receiver, ReceiverImpl>(i->second); + return receiver && (!isDispatch || receiver->getListener()) && accept(receiver.get(), message, isDispatch, transfer); + } +} + +bool SessionImpl::getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout) +{ + qpid::sys::Mutex::ScopedLock l(lock); + return incoming.get(handler, timeout); +} + +bool SessionImpl::dispatch(qpid::sys::Duration timeout) +{ + qpid::messaging::Message message; + IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, true, _1)); + return getIncoming(handler, timeout); +} + +bool SessionImpl::get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + IncomingMessageHandler handler(boost::bind(&SessionImpl::accept, this, &receiver, &message, false, _1)); + return getIncoming(handler, timeout); +} + +bool SessionImpl::fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout) +{ + IncomingMessageHandler handler(boost::bind(&SessionImpl::acceptAny, this, &message, false, _1)); + return getIncoming(handler, timeout); +} + +qpid::messaging::Message SessionImpl::fetch(qpid::sys::Duration timeout) +{ + qpid::messaging::Message result; + if (!fetch(result, timeout)) throw Receiver::NoMessageAvailable(); + return result; +} + +void SessionImpl::receiverCancelled(const std::string& name) +{ + { + qpid::sys::Mutex::ScopedLock l(lock); + receivers.erase(name); + } + session.sync(); + incoming.releasePending(name); +} + +void SessionImpl::senderCancelled(const std::string& name) +{ + qpid::sys::Mutex::ScopedLock l(lock); + senders.erase(name); +} + +void SessionImpl::sync() +{ + session.sync(); +} + +void SessionImpl::flush() +{ + session.flush(); +} + +void* SessionImpl::getLastConfirmedSent() +{ + return 0; +} + +void* SessionImpl::getLastConfirmedAcknowledged() +{ + return 0; +} + +}}} // namespace qpid::client::amqp0_10 diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h new file mode 100644 index 0000000000..6926fb0235 --- /dev/null +++ b/qpid/cpp/src/qpid/client/amqp0_10/SessionImpl.h @@ -0,0 +1,107 @@ +#ifndef QPID_CLIENT_AMQP0_10_SESSIONIMPL_H +#define QPID_CLIENT_AMQP0_10_SESSIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/SessionImpl.h" +#include "qpid/messaging/Variant.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/amqp0_10/AddressResolution.h" +#include "qpid/client/amqp0_10/IncomingMessages.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { + +namespace messaging { +class Address; +class Filter; +class Message; +class Receiver; +class Sender; +class Session; +} + +namespace client { +namespace amqp0_10 { + +class ReceiverImpl; +class SenderImpl; + +/** + * Implementation of the protocol independent Session interface using + * AMQP 0-10. + */ +class SessionImpl : public qpid::messaging::SessionImpl +{ + public: + SessionImpl(qpid::client::Session); + void commit(); + void rollback(); + void acknowledge(); + void reject(qpid::messaging::Message&); + void close(); + void sync(); + void flush(); + qpid::messaging::Address createTempQueue(const std::string& baseName); + qpid::messaging::Sender createSender(const qpid::messaging::Address& address, + const qpid::messaging::VariantMap& options); + qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address, + const qpid::messaging::VariantMap& options); + qpid::messaging::Receiver createReceiver(const qpid::messaging::Address& address, + const qpid::messaging::Filter& filter, + const qpid::messaging::VariantMap& options); + + void* getLastConfirmedSent(); + void* getLastConfirmedAcknowledged(); + + bool fetch(qpid::messaging::Message& message, qpid::sys::Duration timeout); + qpid::messaging::Message fetch(qpid::sys::Duration timeout); + bool dispatch(qpid::sys::Duration timeout); + + bool get(ReceiverImpl& receiver, qpid::messaging::Message& message, qpid::sys::Duration timeout); + + void receiverCancelled(const std::string& name); + void senderCancelled(const std::string& name); + + static SessionImpl& convert(qpid::messaging::Session&); + + qpid::client::Session session; + private: + typedef std::map<std::string, qpid::messaging::Receiver> Receivers; + typedef std::map<std::string, qpid::messaging::Sender> Senders; + + qpid::sys::Mutex lock; + AddressResolution resolver; + IncomingMessages incoming; + Receivers receivers; + Senders senders; + + qpid::messaging::Receiver addReceiver(const qpid::messaging::Address& address, + const qpid::messaging::Filter* filter, + const qpid::messaging::VariantMap& options); + bool acceptAny(qpid::messaging::Message*, bool, IncomingMessages::MessageTransfer&); + bool accept(ReceiverImpl*, qpid::messaging::Message*, bool, IncomingMessages::MessageTransfer&); + bool getIncoming(IncomingMessages::Handler& handler, qpid::sys::Duration timeout); +}; +}}} // namespace qpid::client::amqp0_10 + +#endif /*!QPID_CLIENT_AMQP0_10_SESSIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Address.cpp b/qpid/cpp/src/qpid/messaging/Address.cpp new file mode 100644 index 0000000000..ed35054a00 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Address.cpp @@ -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 "qpid/messaging/Address.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +Address::Address() {} +Address::Address(const std::string& address) : value(address) {} +Address::Address(const std::string& address, const std::string& t) : value(address), type(t) {} +Address::operator const std::string&() const { return value; } +const std::string& Address::toStr() const { return value; } +Address::operator bool() const { return !value.empty(); } +bool Address::operator !() const { return value.empty(); } + +const std::string TYPE_SEPARATOR(":"); + +std::ostream& operator<<(std::ostream& out, const Address& address) +{ + if (!address.type.empty()) { + out << address.type; + out << TYPE_SEPARATOR; + } + out << address.value; + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Connection.cpp b/qpid/cpp/src/qpid/messaging/Connection.cpp new file mode 100644 index 0000000000..feb6566008 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Connection.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 "qpid/messaging/Connection.h" +#include "qpid/messaging/ConnectionImpl.h" +#include "qpid/messaging/Session.h" +#include "qpid/messaging/SessionImpl.h" +#include "qpid/client/PrivateImplRef.h" +#include "qpid/client/amqp0_10/ConnectionImpl.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef<qpid::messaging::Connection> PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Connection Connection::open(const std::string& url, const Variant::Map& options) +{ + //only support amqp 0-10 at present + Connection connection(new qpid::client::amqp0_10::ConnectionImpl(url, options)); + return connection; +} + +Connection::Connection(ConnectionImpl* impl) { PI::ctor(*this, impl); } +Connection::Connection(const Connection& c) : qpid::client::Handle<ConnectionImpl>() { PI::copy(*this, c); } +Connection& Connection::operator=(const Connection& c) { return PI::assign(*this, c); } +Connection::~Connection() { PI::dtor(*this); } + +void Connection::close() { impl->close(); } +Session Connection::newSession() { return impl->newSession(); } + +InvalidOptionString::InvalidOptionString(const std::string& msg) : Exception(msg) {} + +void parseKeyValuePair(const std::string& in, Variant::Map& out) +{ + std::string::size_type i = in.find('='); + if (i == std::string::npos || i == in.size() || in.find('=', i+1) != std::string::npos) { + throw InvalidOptionString(QPID_MSG("Cannot parse name-value pair from " << in)); + } else { + out[in.substr(0, i)] = in.substr(i+1); + } +} + +void parseOptionString(const std::string& in, Variant::Map& out) +{ + std::string::size_type start = 0; + std::string::size_type i = in.find('&'); + while (i != std::string::npos) { + parseKeyValuePair(in.substr(start, i-start), out); + if (i < in.size()) { + start = i+1; + i = in.find('&', start); + } else { + i = std::string::npos; + } + } + parseKeyValuePair(in.substr(start), out); +} + +Variant::Map parseOptionString(const std::string& in) +{ + Variant::Map map; + parseOptionString(in, map); + return map; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/ConnectionImpl.h b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h new file mode 100644 index 0000000000..aa9e5b5fbe --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/ConnectionImpl.h @@ -0,0 +1,45 @@ +#ifndef QPID_MESSAGING_CONNECTIONIMPL_H +#define QPID_MESSAGING_CONNECTIONIMPL_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 "qpid/RefCounted.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Session; + +class ConnectionImpl : public virtual qpid::RefCounted +{ + public: + virtual ~ConnectionImpl() {} + virtual void close() = 0; + virtual Session newSession() = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_CONNECTIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Filter.cpp b/qpid/cpp/src/qpid/messaging/Filter.cpp new file mode 100644 index 0000000000..b06cbdb373 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Filter.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 "qpid/messaging/Filter.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +Filter::Filter(std::string t, std::string pattern) : type(t) { patterns.push_back(pattern); } +Filter::Filter(std::string t, std::string pattern1, std::string pattern2) : type(t) +{ + patterns.push_back(pattern1); + patterns.push_back(pattern2); +} + +const std::string Filter::WILDCARD("WILDCARD"); +const std::string Filter::EXACT_MATCH("EXACT_MATCH"); + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Message.cpp b/qpid/cpp/src/qpid/messaging/Message.cpp new file mode 100644 index 0000000000..e95a05db17 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Message.cpp @@ -0,0 +1,325 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Codec.h" +#include "qpid/messaging/MessageContent.h" +#include "qpid/messaging/Variant.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +namespace { +const std::string EMPTY_STRING = ""; +} + +struct MessageImpl : MessageContent +{ + Address replyTo; + std::string subject; + std::string contentType; + VariantMap headers; + + std::string bytes; + Variant content;//used only for LIST and MAP + VariantType type;//if LIST, MAP content holds the value; if VOID bytes holds the value + + void* internalId; + + MessageImpl(const std::string& c); + MessageImpl(const char* chars, size_t count); + + void setReplyTo(const Address& d); + const Address& getReplyTo() const; + + void setSubject(const std::string& s); + const std::string& getSubject() const; + + void setContentType(const std::string& s); + const std::string& getContentType() const; + + const VariantMap& getHeaders() const; + VariantMap& getHeaders(); + + void setBytes(const std::string& bytes); + void setBytes(const char* chars, size_t count); + const std::string& getBytes() const; + std::string& getBytes(); + + void setInternalId(void*); + void* getInternalId(); + + bool isVoid() const; + + const std::string& asString() const; + std::string& asString(); + + const char* asChars() const; + size_t size() const; + + const Variant::Map& asMap() const; + Variant::Map& asMap(); + bool isMap() const; + + const Variant::List& asList() const; + Variant::List& asList(); + bool isList() const; + + void clear(); + + void encode(Codec& codec); + void decode(Codec& codec); + + Variant& operator[](const std::string&); + + std::ostream& print(std::ostream& out) const; + + //operator<< for variety of types... + MessageContent& operator<<(const std::string&); + MessageContent& operator<<(const char*); + MessageContent& operator<<(bool); + MessageContent& operator<<(int8_t); + MessageContent& operator<<(int16_t); + MessageContent& operator<<(int32_t); + MessageContent& operator<<(int64_t); + MessageContent& operator<<(uint8_t); + MessageContent& operator<<(uint16_t); + MessageContent& operator<<(uint32_t); + MessageContent& operator<<(uint64_t); + MessageContent& operator<<(double); + MessageContent& operator<<(float); + + //assignment from string, map and list + MessageContent& operator=(const std::string&); + MessageContent& operator=(const char*); + MessageContent& operator=(const Variant::Map&); + MessageContent& operator=(const Variant::List&); + + template <class T> MessageContent& append(T& t); +}; + +MessageImpl::MessageImpl(const std::string& c) : bytes(c), type(VOID), internalId(0) {} +MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), type(VOID), internalId(0) {} + +void MessageImpl::setReplyTo(const Address& d) { replyTo = d; } +const Address& MessageImpl::getReplyTo() const { return replyTo; } + +void MessageImpl::setSubject(const std::string& s) { subject = s; } +const std::string& MessageImpl::getSubject() const { return subject; } + +void MessageImpl::setContentType(const std::string& s) { contentType = s; } +const std::string& MessageImpl::getContentType() const { return contentType; } + +const VariantMap& MessageImpl::getHeaders() const { return headers; } +VariantMap& MessageImpl::getHeaders() { return headers; } + +//should these methods be on MessageContent? +void MessageImpl::setBytes(const std::string& c) { clear(); bytes = c; } +void MessageImpl::setBytes(const char* chars, size_t count) { clear(); bytes.assign(chars, count); } +const std::string& MessageImpl::getBytes() const { return bytes; } +std::string& MessageImpl::getBytes() { return bytes; } + + +Variant& MessageImpl::operator[](const std::string& key) { return asMap()[key]; } + +std::ostream& MessageImpl::print(std::ostream& out) const +{ + if (type == MAP) { + return out << content.asMap(); + } else if (type == LIST) { + return out << content.asList(); + } else { + return out << bytes; + } +} + +template <class T> MessageContent& MessageImpl::append(T& t) +{ + if (type == VOID) { + //TODO: this is inefficient, probably want to hold on to the stream object + std::stringstream s; + s << bytes; + s << t; + bytes = s.str(); + } else if (type == LIST) { + content.asList().push_back(Variant(t)); + } else { + throw InvalidConversion("<< operator only valid on strings and lists"); + } + return *this; +} + +MessageContent& MessageImpl::operator<<(const std::string& v) { return append(v); } +MessageContent& MessageImpl::operator<<(const char* v) { return append(v); } +MessageContent& MessageImpl::operator<<(bool v) { return append(v); } +MessageContent& MessageImpl::operator<<(int8_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(int16_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(int32_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(int64_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint8_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint16_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint32_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(uint64_t v) { return append(v); } +MessageContent& MessageImpl::operator<<(double v) { return append(v); } +MessageContent& MessageImpl::operator<<(float v) { return append(v); } +MessageContent& MessageImpl::operator=(const std::string& s) +{ + type = VOID; + bytes = s; + return *this; +} +MessageContent& MessageImpl::operator=(const char* c) +{ + type = VOID; + bytes = c; + return *this; +} +MessageContent& MessageImpl::operator=(const Variant::Map& m) +{ + type = MAP; + content = m; + return *this; +} + +MessageContent& MessageImpl::operator=(const Variant::List& l) +{ + type = LIST; + content = l; + return *this; +} + +void MessageImpl::encode(Codec& codec) +{ + if (content.getType() != VOID) { + bytes = EMPTY_STRING; + codec.encode(content, bytes); + } +} + +void MessageImpl::decode(Codec& codec) +{ + codec.decode(bytes, content); + if (content.getType() == MAP) type = MAP; + else if (content.getType() == LIST) type = LIST; + else type = VOID;//TODO: what if codec set some type other than map or list?? +} + +void MessageImpl::setInternalId(void* i) { internalId = i; } +void* MessageImpl::getInternalId() { return internalId; } + +bool MessageImpl::isVoid() const { return type == VOID; } + +const std::string& MessageImpl::asString() const +{ + if (isVoid()) return getBytes(); + else return content.getString();//will throw an error +} +std::string& MessageImpl::asString() +{ + if (isVoid()) return getBytes(); + else return content.getString();//will throw an error +} + +const char* MessageImpl::asChars() const +{ + if (!isVoid()) throw InvalidConversion("Content is of structured type."); + return bytes.data(); +} +size_t MessageImpl::size() const +{ + return bytes.size(); +} + +const Variant::Map& MessageImpl::asMap() const { return content.asMap(); } +Variant::Map& MessageImpl::asMap() +{ + if (isVoid()) { + content = Variant::Map(); + type = MAP; + } + return content.asMap(); +} +bool MessageImpl::isMap() const { return type == MAP; } + +const Variant::List& MessageImpl::asList() const { return content.asList(); } +Variant::List& MessageImpl::asList() +{ + if (isVoid()) { + content = Variant::List(); + type = LIST; + } + return content.asList(); +} +bool MessageImpl::isList() const { return type == LIST; } + +void MessageImpl::clear() { bytes = EMPTY_STRING; content.reset(); type = VOID; } + + +Message::Message(const std::string& bytes) : impl(new MessageImpl(bytes)) {} +Message::Message(const char* bytes, size_t count) : impl(new MessageImpl(bytes, count)) {} + +Message::Message(const Message& m) : impl(new MessageImpl(m.getBytes())) {} +Message::~Message() { delete impl; } + +Message& Message::operator=(const Message& m) { *impl = *m.impl; return *this; } + +void Message::setReplyTo(const Address& d) { impl->setReplyTo(d); } +const Address& Message::getReplyTo() const { return impl->getReplyTo(); } + +void Message::setSubject(const std::string& s) { impl->setSubject(s); } +const std::string& Message::getSubject() const { return impl->getSubject(); } + +void Message::setContentType(const std::string& s) { impl->setContentType(s); } +const std::string& Message::getContentType() const { return impl->getContentType(); } + +const VariantMap& Message::getHeaders() const { return impl->getHeaders(); } +VariantMap& Message::getHeaders() { return impl->getHeaders(); } + +void Message::setBytes(const std::string& c) { impl->setBytes(c); } +void Message::setBytes(const char* chars, size_t count) { impl->setBytes(chars, count); } +const std::string& Message::getBytes() const { return impl->getBytes(); } +std::string& Message::getBytes() { return impl->getBytes(); } + +const char* Message::getRawContent() const { return impl->getBytes().data(); } +size_t Message::getContentSize() const { return impl->getBytes().size(); } + +MessageContent& Message::getContent() { return *impl; } +const MessageContent& Message::getContent() const { return *impl; } +void Message::setContent(const std::string& s) { *impl = s; } +void Message::setContent(const Variant::Map& m) { *impl = m; } +void Message::setContent(const Variant::List& l) { *impl = l; } + +void Message::encode(Codec& codec) { impl->encode(codec); } + +void Message::decode(Codec& codec) { impl->decode(codec); } + +void Message::setInternalId(void* i) { impl->setInternalId(i); } +void* Message::getInternalId() { return impl->getInternalId(); } + +std::ostream& operator<<(std::ostream& out, const MessageContent& content) +{ + return content.print(out); +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/Receiver.cpp b/qpid/cpp/src/qpid/messaging/Receiver.cpp new file mode 100644 index 0000000000..2e8b89d27f --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Receiver.cpp @@ -0,0 +1,51 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/ReceiverImpl.h" +#include "qpid/client/PrivateImplRef.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef<qpid::messaging::Receiver> PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Receiver::Receiver(ReceiverImpl* impl) { PI::ctor(*this, impl); } +Receiver::Receiver(const Receiver& s) : qpid::client::Handle<ReceiverImpl>() { PI::copy(*this, s); } +Receiver::~Receiver() { PI::dtor(*this); } +Receiver& Receiver::operator=(const Receiver& s) { return PI::assign(*this, s); } +bool Receiver::get(Message& message, qpid::sys::Duration timeout) { return impl->get(message, timeout); } +Message Receiver::get(qpid::sys::Duration timeout) { return impl->get(timeout); } +bool Receiver::fetch(Message& message, qpid::sys::Duration timeout) { return impl->fetch(message, timeout); } +Message Receiver::fetch(qpid::sys::Duration timeout) { return impl->fetch(timeout); } +void Receiver::start() { impl->start(); } +void Receiver::stop() { impl->stop(); } +void Receiver::setCapacity(uint32_t c) { impl->setCapacity(c); } +void Receiver::cancel() { impl->cancel(); } +void Receiver::setListener(MessageListener* listener) { impl->setListener(listener); } + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h new file mode 100644 index 0000000000..77697b730c --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h @@ -0,0 +1,52 @@ +#ifndef QPID_MESSAGING_RECEIVERIMPL_H +#define QPID_MESSAGING_RECEIVERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/RefCounted.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Message; +class MessageListener; + +class ReceiverImpl : public virtual qpid::RefCounted +{ + public: + virtual ~ReceiverImpl() {} + virtual bool get(Message& message, qpid::sys::Duration timeout) = 0; + virtual Message get(qpid::sys::Duration timeout) = 0; + virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0; + virtual Message fetch(qpid::sys::Duration timeout) = 0; + virtual void start() = 0; + virtual void stop() = 0; + virtual void setCapacity(uint32_t) = 0; + virtual void cancel() = 0; + virtual void setListener(MessageListener*) = 0; +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_RECEIVERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Sender.cpp b/qpid/cpp/src/qpid/messaging/Sender.cpp new file mode 100644 index 0000000000..12a3a8eb0f --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Sender.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 "qpid/messaging/Sender.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/SenderImpl.h" +#include "qpid/client/PrivateImplRef.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef<qpid::messaging::Sender> PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Sender::Sender(SenderImpl* impl) { PI::ctor(*this, impl); } +Sender::Sender(const Sender& s) : qpid::client::Handle<SenderImpl>() { PI::copy(*this, s); } +Sender::~Sender() { PI::dtor(*this); } +Sender& Sender::operator=(const Sender& s) { return PI::assign(*this, s); } +void Sender::send(Message& message) { impl->send(message); } +void Sender::cancel() { impl->cancel(); } + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SenderImpl.h b/qpid/cpp/src/qpid/messaging/SenderImpl.h new file mode 100644 index 0000000000..3b61a37423 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h @@ -0,0 +1,44 @@ +#ifndef QPID_MESSAGING_SENDERIMPL_H +#define QPID_MESSAGING_SENDERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/RefCounted.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Message; + +class SenderImpl : public virtual qpid::RefCounted +{ + public: + virtual ~SenderImpl() {} + virtual void send(Message& message) = 0; + virtual void cancel() = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_SENDERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Session.cpp b/qpid/cpp/src/qpid/messaging/Session.cpp new file mode 100644 index 0000000000..284b20dacc --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Session.cpp @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Session.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Filter.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/SessionImpl.h" +#include "qpid/client/PrivateImplRef.h" + +namespace qpid { +namespace client { + +typedef PrivateImplRef<qpid::messaging::Session> PI; + +} + +namespace messaging { + +using qpid::client::PI; + +Session::Session(SessionImpl* impl) { PI::ctor(*this, impl); } +Session::Session(const Session& s) : qpid::client::Handle<SessionImpl>() { PI::copy(*this, s); } +Session::~Session() { PI::dtor(*this); } +Session& Session::operator=(const Session& s) { return PI::assign(*this, s); } +void Session::commit() { impl->commit(); } +void Session::rollback() { impl->rollback(); } +void Session::acknowledge() { impl->acknowledge(); } +void Session::reject(Message& m) { impl->reject(m); } +void Session::close() { impl->close(); } + +Sender Session::createSender(const Address& address, const VariantMap& options) +{ + return impl->createSender(address, options); +} +Receiver Session::createReceiver(const Address& address, const VariantMap& options) +{ + return impl->createReceiver(address, options); +} +Receiver Session::createReceiver(const Address& address, const Filter& filter, const VariantMap& options) +{ + return impl->createReceiver(address, filter, options); +} + +Sender Session::createSender(const std::string& address, const VariantMap& options) +{ + return impl->createSender(Address(address), options); +} +Receiver Session::createReceiver(const std::string& address, const VariantMap& options) +{ + return impl->createReceiver(Address(address), options); +} +Receiver Session::createReceiver(const std::string& address, const Filter& filter, const VariantMap& options) +{ + return impl->createReceiver(Address(address), filter, options); +} + +Address Session::createTempQueue(const std::string& baseName) +{ + return impl->createTempQueue(baseName); +} + +void Session::sync() +{ + impl->sync(); +} + +void Session::flush() +{ + impl->flush(); +} + +bool Session::fetch(Message& message, qpid::sys::Duration timeout) +{ + return impl->fetch(message, timeout); +} + +Message Session::fetch(qpid::sys::Duration timeout) +{ + return impl->fetch(timeout); +} + +bool Session::dispatch(qpid::sys::Duration timeout) +{ + return impl->dispatch(timeout); +} + +void* Session::getLastConfirmedSent() +{ + return impl->getLastConfirmedSent(); +} + +void* Session::getLastConfirmedAcknowledged() +{ + return impl->getLastConfirmedAcknowledged(); +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/qpid/messaging/SessionImpl.h b/qpid/cpp/src/qpid/messaging/SessionImpl.h new file mode 100644 index 0000000000..7a7ce731f8 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/SessionImpl.h @@ -0,0 +1,65 @@ +#ifndef QPID_MESSAGING_SESSIONIMPL_H +#define QPID_MESSAGING_SESSIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/RefCounted.h" +#include <string> +#include "qpid/messaging/Variant.h" +#include "qpid/sys/Time.h" + +namespace qpid { +namespace client { +} + +namespace messaging { + +class Address; +class Filter; +class Message; +class Sender; +class Receiver; + +class SessionImpl : public virtual qpid::RefCounted +{ + public: + virtual ~SessionImpl() {} + virtual void commit() = 0; + virtual void rollback() = 0; + virtual void acknowledge() = 0; + virtual void reject(Message&) = 0; + virtual void close() = 0; + virtual void sync() = 0; + virtual void flush() = 0; + virtual bool fetch(Message& message, qpid::sys::Duration timeout) = 0; + virtual Message fetch(qpid::sys::Duration timeout) = 0; + virtual bool dispatch(qpid::sys::Duration timeout) = 0; + virtual Address createTempQueue(const std::string& baseName) = 0; + virtual Sender createSender(const Address& address, const VariantMap& options) = 0; + virtual Receiver createReceiver(const Address& address, const VariantMap& options) = 0; + virtual Receiver createReceiver(const Address& address, const Filter& filter, const VariantMap& options) = 0; + virtual void* getLastConfirmedSent() = 0; + virtual void* getLastConfirmedAcknowledged() = 0; + private: +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_SESSIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/Variant.cpp b/qpid/cpp/src/qpid/messaging/Variant.cpp new file mode 100644 index 0000000000..59770939e1 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Variant.cpp @@ -0,0 +1,603 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/Variant.h" +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> + +namespace qpid { +namespace client { +} + +namespace messaging { + +InvalidConversion::InvalidConversion(const std::string& msg) : Exception(msg) {} + + +namespace { +std::string EMPTY; +} + +class VariantImpl +{ + public: + VariantImpl(); + VariantImpl(bool); + VariantImpl(uint8_t); + VariantImpl(uint16_t); + VariantImpl(uint32_t); + VariantImpl(uint64_t); + VariantImpl(int8_t); + VariantImpl(int16_t); + VariantImpl(int32_t); + VariantImpl(int64_t); + VariantImpl(float); + VariantImpl(double); + VariantImpl(const std::string&); + VariantImpl(const Variant::Map&); + VariantImpl(const Variant::List&); + ~VariantImpl(); + + VariantType getType() const; + + bool asBool() const; + uint8_t asUint8() const; + uint16_t asUint16() const; + uint32_t asUint32() const; + uint64_t asUint64() const; + int8_t asInt8() const; + int16_t asInt16() const; + int32_t asInt32() const; + int64_t asInt64() const; + float asFloat() const; + double asDouble() const; + std::string asString() const; + + const Variant::Map& asMap() const; + Variant::Map& asMap(); + const Variant::List& asList() const; + Variant::List& asList(); + + const std::string& getString() const; + std::string& getString(); + + void setEncoding(const std::string&); + const std::string& getEncoding() const; + + static VariantImpl* create(const Variant&); + private: + const VariantType type; + union { + bool b; + uint8_t ui8; + uint16_t ui16; + uint32_t ui32; + uint64_t ui64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + float f; + double d; + void* v;//variable width data + } value; + std::string encoding;//optional encoding for variable length data + + std::string getTypeName(VariantType type) const; + template<class T> T convertFromString() const + { + std::string* s = reinterpret_cast<std::string*>(value.v); + try { + return boost::lexical_cast<T>(*s); + } catch(const boost::bad_lexical_cast&) { + throw InvalidConversion(QPID_MSG("Cannot convert " << *s)); + } + } +}; + + +VariantImpl::VariantImpl() : type(VOID) { value.i64 = 0; } +VariantImpl::VariantImpl(bool b) : type(BOOL) { value.b = b; } +VariantImpl::VariantImpl(uint8_t i) : type(UINT8) { value.ui8 = i; } +VariantImpl::VariantImpl(uint16_t i) : type(UINT16) { value.ui16 = i; } +VariantImpl::VariantImpl(uint32_t i) : type(UINT32) { value.ui32 = i; } +VariantImpl::VariantImpl(uint64_t i) : type(UINT64) { value.ui64 = i; } +VariantImpl::VariantImpl(int8_t i) : type(INT8) { value.i8 = i; } +VariantImpl::VariantImpl(int16_t i) : type(INT16) { value.i16 = i; } +VariantImpl::VariantImpl(int32_t i) : type(INT32) { value.i32 = i; } +VariantImpl::VariantImpl(int64_t i) : type(INT64) { value.i64 = i; } +VariantImpl::VariantImpl(float f) : type(FLOAT) { value.f = f; } +VariantImpl::VariantImpl(double d) : type(DOUBLE) { value.d = d; } +VariantImpl::VariantImpl(const std::string& s) : type(STRING) { value.v = new std::string(s); } +VariantImpl::VariantImpl(const Variant::Map& m) : type(MAP) { value.v = new Variant::Map(m); } +VariantImpl::VariantImpl(const Variant::List& l) : type(LIST) { value.v = new Variant::List(l); } + +VariantImpl::~VariantImpl() { + switch (type) { + case STRING: + delete reinterpret_cast<std::string*>(value.v); + break; + case MAP: + delete reinterpret_cast<Variant::Map*>(value.v); + break; + case LIST: + delete reinterpret_cast<Variant::List*>(value.v); + break; + default: + break; + } +} + +VariantType VariantImpl::getType() const { return type; } + +namespace { + +bool same_char(char a, char b) +{ + return toupper(a) == toupper(b); +} + +bool caseInsensitiveMatch(const std::string& a, const std::string& b) +{ + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), &same_char); +} + +const std::string TRUE("True"); +const std::string FALSE("False"); + +bool toBool(const std::string& s) +{ + if (caseInsensitiveMatch(s, TRUE)) return true; + if (caseInsensitiveMatch(s, FALSE)) return false; + try { return boost::lexical_cast<int>(s); } catch(const boost::bad_lexical_cast&) {} + throw InvalidConversion(QPID_MSG("Cannot convert " << s << " to bool")); +} + +} + +bool VariantImpl::asBool() const +{ + switch(type) { + case VOID: return false; + case BOOL: return value.b; + case UINT8: return value.ui8; + case UINT16: return value.ui16; + case UINT32: return value.ui32; + case UINT64: return value.ui64; + case INT8: return value.i8; + case INT16: return value.i16; + case INT32: return value.i32; + case INT64: return value.i64; + case STRING: return toBool(*reinterpret_cast<std::string*>(value.v)); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(BOOL))); + } +} +uint8_t VariantImpl::asUint8() const +{ + switch(type) { + case UINT8: return value.ui8; + case STRING: return convertFromString<uint8_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT8))); + } +} +uint16_t VariantImpl::asUint16() const +{ + switch(type) { + case UINT8: return value.ui8; + case UINT16: return value.ui16; + case STRING: return convertFromString<uint16_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT16))); + } +} +uint32_t VariantImpl::asUint32() const +{ + switch(type) { + case UINT8: return value.ui8; + case UINT16: return value.ui16; + case UINT32: return value.ui32; + case STRING: return convertFromString<uint32_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT32))); + } +} +uint64_t VariantImpl::asUint64() const +{ + switch(type) { + case UINT8: return value.ui8; + case UINT16: return value.ui16; + case UINT32: return value.ui32; + case UINT64: return value.ui64; + case STRING: return convertFromString<uint64_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(UINT64))); + } +} +int8_t VariantImpl::asInt8() const +{ + switch(type) { + case INT8: return value.i8; + case STRING: return convertFromString<int8_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT8))); + } +} +int16_t VariantImpl::asInt16() const +{ + switch(type) { + case INT8: return value.i8; + case INT16: return value.i16; + case STRING: return convertFromString<int16_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT16))); + } +} +int32_t VariantImpl::asInt32() const +{ + switch(type) { + case INT8: return value.i8; + case INT16: return value.i16; + case INT32: return value.i32; + case STRING: return convertFromString<int32_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT32))); + } +} +int64_t VariantImpl::asInt64() const +{ + switch(type) { + case INT8: return value.i8; + case INT16: return value.i16; + case INT32: return value.i32; + case INT64: return value.i64; + case STRING: return convertFromString<int64_t>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(INT64))); + } +} +float VariantImpl::asFloat() const +{ + switch(type) { + case FLOAT: return value.f; + case STRING: return convertFromString<float>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(FLOAT))); + } +} +double VariantImpl::asDouble() const +{ + switch(type) { + case FLOAT: return value.f; + case DOUBLE: return value.d; + case STRING: return convertFromString<double>(); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(DOUBLE))); + } +} +std::string VariantImpl::asString() const +{ + switch(type) { + case VOID: return EMPTY; + case BOOL: return value.b ? TRUE : FALSE; + case UINT8: return boost::lexical_cast<std::string>((int) value.ui8); + case UINT16: return boost::lexical_cast<std::string>(value.ui16); + case UINT32: return boost::lexical_cast<std::string>(value.ui32); + case UINT64: return boost::lexical_cast<std::string>(value.ui64); + case INT8: return boost::lexical_cast<std::string>((int) value.i8); + case INT16: return boost::lexical_cast<std::string>(value.i16); + case INT32: return boost::lexical_cast<std::string>(value.i32); + case INT64: return boost::lexical_cast<std::string>(value.i64); + case DOUBLE: return boost::lexical_cast<std::string>(value.d); + case FLOAT: return boost::lexical_cast<std::string>(value.f); + case STRING: return *reinterpret_cast<std::string*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(STRING))); + } +} + +const Variant::Map& VariantImpl::asMap() const +{ + switch(type) { + case MAP: return *reinterpret_cast<Variant::Map*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(MAP))); + } +} + +Variant::Map& VariantImpl::asMap() +{ + switch(type) { + case MAP: return *reinterpret_cast<Variant::Map*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(MAP))); + } +} + +const Variant::List& VariantImpl::asList() const +{ + switch(type) { + case LIST: return *reinterpret_cast<Variant::List*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(LIST))); + } +} + +Variant::List& VariantImpl::asList() +{ + switch(type) { + case LIST: return *reinterpret_cast<Variant::List*>(value.v); + default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(LIST))); + } +} + +std::string& VariantImpl::getString() +{ + switch(type) { + case STRING: return *reinterpret_cast<std::string*>(value.v); + default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required.")); + } +} + +const std::string& VariantImpl::getString() const +{ + switch(type) { + case STRING: return *reinterpret_cast<std::string*>(value.v); + default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required.")); + } +} + +void VariantImpl::setEncoding(const std::string& s) { encoding = s; } +const std::string& VariantImpl::getEncoding() const { return encoding; } + +std::string VariantImpl::getTypeName(VariantType type) const +{ + switch (type) { + case VOID: return "void"; + case BOOL: return "bool"; + case UINT8: return "uint8"; + case UINT16: return "uint16"; + case UINT32: return "uint32"; + case UINT64: return "uint64"; + case INT8: return "int8"; + case INT16: return "int16"; + case INT32: return "int32"; + case INT64: return "int64"; + case FLOAT: return "float"; + case DOUBLE: return "double"; + case STRING: return "string"; + case MAP: return "map"; + case LIST: return "list"; + } + return "<unknown>";//should never happen +} + +VariantImpl* VariantImpl::create(const Variant& v) +{ + switch (v.getType()) { + case BOOL: return new VariantImpl(v.asBool()); + case UINT8: return new VariantImpl(v.asUint8()); + case UINT16: return new VariantImpl(v.asUint16()); + case UINT32: return new VariantImpl(v.asUint32()); + case UINT64: return new VariantImpl(v.asUint64()); + case INT8: return new VariantImpl(v.asInt8()); + case INT16: return new VariantImpl(v.asInt16()); + case INT32: return new VariantImpl(v.asInt32()); + case INT64: return new VariantImpl(v.asInt64()); + case FLOAT: return new VariantImpl(v.asFloat()); + case DOUBLE: return new VariantImpl(v.asDouble()); + case STRING: return new VariantImpl(v.asString()); + case MAP: return new VariantImpl(v.asMap()); + case LIST: return new VariantImpl(v.asList()); + default: return new VariantImpl(); + } +} + +Variant::Variant() : impl(new VariantImpl()) {} +Variant::Variant(bool b) : impl(new VariantImpl(b)) {} +Variant::Variant(uint8_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(uint16_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(uint32_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(uint64_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int8_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int16_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int32_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(int64_t i) : impl(new VariantImpl(i)) {} +Variant::Variant(float f) : impl(new VariantImpl(f)) {} +Variant::Variant(double d) : impl(new VariantImpl(d)) {} +Variant::Variant(const std::string& s) : impl(new VariantImpl(s)) {} +Variant::Variant(const char* s) : impl(new VariantImpl(std::string(s))) {} +Variant::Variant(const Map& m) : impl(new VariantImpl(m)) {} +Variant::Variant(const List& l) : impl(new VariantImpl(l)) {} +Variant::Variant(const Variant& v) : impl(VariantImpl::create(v)) {} + +Variant::~Variant() { if (impl) delete impl; } + +void Variant::reset() +{ + if (impl) delete impl; + impl = new VariantImpl(); +} + + +Variant& Variant::operator=(bool b) +{ + if (impl) delete impl; + impl = new VariantImpl(b); + return *this; +} + +Variant& Variant::operator=(uint8_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(uint16_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(uint32_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(uint64_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} + +Variant& Variant::operator=(int8_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(int16_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(int32_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} +Variant& Variant::operator=(int64_t i) +{ + if (impl) delete impl; + impl = new VariantImpl(i); + return *this; +} + +Variant& Variant::operator=(float f) +{ + if (impl) delete impl; + impl = new VariantImpl(f); + return *this; +} +Variant& Variant::operator=(double d) +{ + if (impl) delete impl; + impl = new VariantImpl(d); + return *this; +} + +Variant& Variant::operator=(const std::string& s) +{ + if (impl) delete impl; + impl = new VariantImpl(s); + return *this; +} + +Variant& Variant::operator=(const char* s) +{ + if (impl) delete impl; + impl = new VariantImpl(std::string(s)); + return *this; +} + +Variant& Variant::operator=(const Map& m) +{ + if (impl) delete impl; + impl = new VariantImpl(m); + return *this; +} + +Variant& Variant::operator=(const List& l) +{ + if (impl) delete impl; + impl = new VariantImpl(l); + return *this; +} + +Variant& Variant::operator=(const Variant& v) +{ + if (impl) delete impl; + impl = VariantImpl::create(v); + return *this; +} + +VariantType Variant::getType() const { return impl->getType(); } +bool Variant::asBool() const { return impl->asBool(); } +uint8_t Variant::asUint8() const { return impl->asUint8(); } +uint16_t Variant::asUint16() const { return impl->asUint16(); } +uint32_t Variant::asUint32() const { return impl->asUint32(); } +uint64_t Variant::asUint64() const { return impl->asUint64(); } +int8_t Variant::asInt8() const { return impl->asInt8(); } +int16_t Variant::asInt16() const { return impl->asInt16(); } +int32_t Variant::asInt32() const { return impl->asInt32(); } +int64_t Variant::asInt64() const { return impl->asInt64(); } +float Variant::asFloat() const { return impl->asFloat(); } +double Variant::asDouble() const { return impl->asDouble(); } +std::string Variant::asString() const { return impl->asString(); } +const Variant::Map& Variant::asMap() const { return impl->asMap(); } +Variant::Map& Variant::asMap() { return impl->asMap(); } +const Variant::List& Variant::asList() const { return impl->asList(); } +Variant::List& Variant::asList() { return impl->asList(); } +const std::string& Variant::getString() const { return impl->getString(); } +std::string& Variant::getString() { return impl->getString(); } +void Variant::setEncoding(const std::string& s) { impl->setEncoding(s); } +const std::string& Variant::getEncoding() const { return impl->getEncoding(); } + +Variant::operator bool() const { return asBool(); } +Variant::operator uint8_t() const { return asUint8(); } +Variant::operator uint16_t() const { return asUint16(); } +Variant::operator uint32_t() const { return asUint32(); } +Variant::operator uint64_t() const { return asUint64(); } +Variant::operator int8_t() const { return asInt8(); } +Variant::operator int16_t() const { return asInt16(); } +Variant::operator int32_t() const { return asInt32(); } +Variant::operator int64_t() const { return asInt64(); } +Variant::operator float() const { return asFloat(); } +Variant::operator double() const { return asDouble(); } +Variant::operator const char*() const { return asString().c_str(); } + +std::ostream& operator<<(std::ostream& out, const Variant::Map& map) +{ + for (Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) { + if (i != map.begin()) out << ", "; + out << i->first << ":" << i->second; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, const Variant::List& list) +{ + for (Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) { + if (i != list.begin()) out << ", "; + out << *i; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, const Variant& value) +{ + switch (value.getType()) { + case MAP: + out << "{" << value.asMap() << "}"; + break; + case LIST: + out << "[" << value.asList() << "]"; + break; + case VOID: + out << "<void>"; + break; + default: + out << value.asString(); + break; + } + return out; +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt index 34f5d35a9a..56a4aaf2b0 100644 --- a/qpid/cpp/src/tests/CMakeLists.txt +++ b/qpid/cpp/src/tests/CMakeLists.txt @@ -95,6 +95,7 @@ set(unit_tests_to_build InlineAllocator InlineVector ClientSessionTest + MessagingSessionTest SequenceSet StringUtils IncompleteMessageList @@ -128,6 +129,7 @@ set(unit_tests_to_build ReplicationTest ClientMessageTest PollableCondition + Variant ${xml_tests} CACHE STRING "Which unit tests to build" ) diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index db4c8ba914..2e04c85b93 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -65,6 +65,7 @@ unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \ $(lib_client) $(lib_broker) $(lib_console) unit_test_SOURCES= unit_test.cpp unit_test.h \ + MessagingSessionTests.cpp \ ClientSessionTest.cpp \ BrokerFixture.h SocketProxy.h \ exception_test.cpp \ @@ -111,7 +112,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ FrameDecoder.cpp \ ReplicationTest.cpp \ ClientMessageTest.cpp \ - PollableCondition.cpp + PollableCondition.cpp \ + Variant.cpp if HAVE_XML unit_test_SOURCES+= XmlClientSessionTest.cpp diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp new file mode 100644 index 0000000000..ef320c3ae0 --- /dev/null +++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp @@ -0,0 +1,325 @@ +/* + * + * 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 "unit_test.h" +#include "test_tools.h" +#include "BrokerFixture.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageListener.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Session.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Session.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/Time.h" +#include <boost/assign.hpp> +#include <boost/format.hpp> +#include <string> +#include <vector> + +QPID_AUTO_TEST_SUITE(MessagingSessionTests) + +using namespace qpid::messaging; +using namespace qpid; +using qpid::broker::Broker; + +struct BrokerAdmin +{ + qpid::client::Connection connection; + qpid::client::Session session; + + BrokerAdmin(uint16_t port) + { + connection.open("localhost", port); + session = connection.newSession(); + } + + void createQueue(const std::string& name) + { + session.queueDeclare(qpid::client::arg::queue=name); + } + + void deleteQueue(const std::string& name) + { + session.queueDelete(qpid::client::arg::queue=name); + } + + void createExchange(const std::string& name, const std::string& type) + { + session.exchangeDeclare(qpid::client::arg::exchange=name, qpid::client::arg::type=type); + } + + void deleteExchange(const std::string& name) + { + session.exchangeDelete(qpid::client::arg::exchange=name); + } + + ~BrokerAdmin() + { + session.close(); + connection.close(); + } +}; + +struct MessagingFixture : public BrokerFixture +{ + Connection connection; + Session session; + BrokerAdmin admin; + + MessagingFixture(Broker::Options opts = Broker::Options()) : + BrokerFixture(opts), + connection(Connection::open((boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(Broker::TCP_TRANSPORT))).str())), + session(connection.newSession()), + admin(broker->getPort(Broker::TCP_TRANSPORT)) {} + + ~MessagingFixture() + { + session.close(); + connection.close(); + } +}; + +struct QueueFixture : MessagingFixture +{ + std::string queue; + + QueueFixture(const std::string& name = "test-queue") : queue(name) + { + admin.createQueue(queue); + } + + ~QueueFixture() + { + admin.deleteQueue(queue); + } + +}; + +struct TopicFixture : MessagingFixture +{ + std::string topic; + + TopicFixture(const std::string& name = "test-topic", const std::string& type="fanout") : topic(name) + { + admin.createExchange(topic, type); + } + + ~TopicFixture() + { + admin.deleteExchange(topic); + } + +}; + +struct MultiQueueFixture : MessagingFixture +{ + typedef std::vector<std::string>::const_iterator const_iterator; + std::vector<std::string> queues; + + MultiQueueFixture(const std::vector<std::string>& names = boost::assign::list_of<std::string>("q1")("q2")("q3")) : queues(names) + { + for (const_iterator i = queues.begin(); i != queues.end(); ++i) { + admin.createQueue(*i); + } + } + + ~MultiQueueFixture() + { + for (const_iterator i = queues.begin(); i != queues.end(); ++i) { + admin.deleteQueue(*i); + } + } + +}; + +struct MessageDataCollector : MessageListener +{ + std::vector<std::string> messageData; + + void received(Message& message) { + messageData.push_back(message.getBytes()); + } +}; + +std::vector<std::string> fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5) +{ + std::vector<std::string> data; + Message message; + for (int i = 0; i < count && receiver.fetch(message, timeout); i++) { + data.push_back(message.getBytes()); + } + return data; +} + +QPID_AUTO_TEST_CASE(testSimpleSendReceive) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out("test-message"); + sender.send(out); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + fix.session.acknowledge(); + BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); +} + +QPID_AUTO_TEST_CASE(testSenderError) +{ + MessagingFixture fix; + //TODO: this is the wrong type for the exception; define explicit set in messaging namespace + BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress"), qpid::framing::NotFoundException); +} + +QPID_AUTO_TEST_CASE(testReceiverError) +{ + MessagingFixture fix; + //TODO: this is the wrong type for the exception; define explicit set in messaging namespace + BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress"), qpid::framing::NotFoundException); +} + +QPID_AUTO_TEST_CASE(testSimpleTopic) +{ + TopicFixture fix; + + Sender sender = fix.session.createSender(fix.topic); + Message msg("one"); + sender.send(msg); + Receiver sub1 = fix.session.createReceiver(fix.topic); + sub1.setCapacity(10u); + sub1.start(); + msg.setBytes("two"); + sender.send(msg); + Receiver sub2 = fix.session.createReceiver(fix.topic); + sub2.setCapacity(10u); + sub2.start(); + msg.setBytes("three"); + sender.send(msg); + Receiver sub3 = fix.session.createReceiver(fix.topic); + sub3.setCapacity(10u); + sub3.start(); + msg.setBytes("four"); + sender.send(msg); + BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of<std::string>("three")("four")); + sub2.cancel(); + + msg.setBytes("five"); + sender.send(msg); + BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of<std::string>("two")("three")("four")("five")); + BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of<std::string>("four")("five")); + Message in; + BOOST_CHECK(!sub2.fetch(in, 0));//TODO: or should this raise an error? + + + //TODO: check pending messages... +} + +QPID_AUTO_TEST_CASE(testSessionFetch) +{ + MultiQueueFixture fix; + + for (uint i = 0; i < fix.queues.size(); i++) { + Receiver r = fix.session.createReceiver(fix.queues[i]); + r.setCapacity(10u); + r.start();//TODO: add Session::start + } + + for (uint i = 0; i < fix.queues.size(); i++) { + Sender s = fix.session.createSender(fix.queues[i]); + Message msg((boost::format("Message_%1%") % (i+1)).str()); + s.send(msg); + } + + for (uint i = 0; i < fix.queues.size(); i++) { + Message msg; + BOOST_CHECK(fix.session.fetch(msg, qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(msg.getBytes(), (boost::format("Message_%1%") % (i+1)).str()); + } +} + +QPID_AUTO_TEST_CASE(testSessionDispatch) +{ + MultiQueueFixture fix; + + MessageDataCollector collector; + for (uint i = 0; i < fix.queues.size(); i++) { + Receiver r = fix.session.createReceiver(fix.queues[i]); + r.setListener(&collector); + r.setCapacity(10u); + r.start();//TODO: add Session::start + } + + for (uint i = 0; i < fix.queues.size(); i++) { + Sender s = fix.session.createSender(fix.queues[i]); + Message msg((boost::format("Message_%1%") % (i+1)).str()); + s.send(msg); + } + + while (fix.session.dispatch(qpid::sys::TIME_SEC)); + + BOOST_CHECK_EQUAL(collector.messageData, boost::assign::list_of<std::string>("Message_1")("Message_2")("Message_3")); +} + + +QPID_AUTO_TEST_CASE(testMapMessage) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out; + out.getContent().asMap()["abc"] = "def"; + out.getContent().asMap()["pi"] = 3.14f; + sender.send(out); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); + BOOST_CHECK_EQUAL(in.getContent().asMap()["abc"].asString(), "def"); + BOOST_CHECK_EQUAL(in.getContent().asMap()["pi"].asFloat(), 3.14f); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_CASE(testListMessage) +{ + QueueFixture fix; + Sender sender = fix.session.createSender(fix.queue); + Message out; + out.getContent() = Variant::List(); + out.getContent() << "abc"; + out.getContent() << 1234; + out.getContent() << "def"; + out.getContent() << 56.789; + sender.send(out); + Receiver receiver = fix.session.createReceiver(fix.queue); + Message in = receiver.fetch(5 * qpid::sys::TIME_SEC); + BOOST_CHECK_EQUAL(in.getBytes(), out.getBytes()); + Variant::List& list = in.getContent().asList(); + BOOST_CHECK_EQUAL(list.size(), out.getContent().asList().size()); + BOOST_CHECK_EQUAL(list.front().asString(), "abc"); + list.pop_front(); + BOOST_CHECK_EQUAL(list.front().asInt64(), 1234); + list.pop_front(); + BOOST_CHECK_EQUAL(list.front().asString(), "def"); + list.pop_front(); + BOOST_CHECK_EQUAL(list.front().asDouble(), 56.789); + fix.session.acknowledge(); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp new file mode 100644 index 0000000000..1bf2ed98ce --- /dev/null +++ b/qpid/cpp/src/tests/Variant.cpp @@ -0,0 +1,157 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <iostream> +#include "qpid/messaging/Variant.h" + +#include "unit_test.h" + +using namespace qpid::messaging; + +QPID_AUTO_TEST_SUITE(VariantSuite) + +QPID_AUTO_TEST_CASE(testConversions) +{ + Variant value; + + //string to float/double + value = "1.5"; + BOOST_CHECK_EQUAL((float) 1.5, value.asFloat()); + BOOST_CHECK_EQUAL((double) 1.5, value.asDouble()); + + //float to string or double + value = 1.5f; + BOOST_CHECK_EQUAL((float) 1.5, value.asFloat()); + BOOST_CHECK_EQUAL((double) 1.5, value.asDouble()); + BOOST_CHECK_EQUAL(std::string("1.5"), value.asString()); + + //double to string (conversion to float not valid) + value = 1.5; + BOOST_CHECK_THROW(value.asFloat(), InvalidConversion); + BOOST_CHECK_EQUAL((double) 1.5, value.asDouble()); + BOOST_CHECK_EQUAL(std::string("1.5"), value.asString()); + + //uint8 to larger unsigned ints and string + value = (uint8_t) 7; + BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8()); + BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16()); + BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32()); + BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64()); + BOOST_CHECK_EQUAL(std::string("7"), value.asString()); + BOOST_CHECK_THROW(value.asInt8(), InvalidConversion); + + value = (uint16_t) 8; + BOOST_CHECK_EQUAL(std::string("8"), value.asString()); + value = (uint32_t) 9; + BOOST_CHECK_EQUAL(std::string("9"), value.asString()); + + //uint32 to larger unsigned ints and string + value = (uint32_t) 9999999; + BOOST_CHECK_EQUAL((uint32_t) 9999999, value.asUint32()); + BOOST_CHECK_EQUAL((uint64_t) 9999999, value.asUint64()); + BOOST_CHECK_EQUAL(std::string("9999999"), value.asString()); + BOOST_CHECK_THROW(value.asUint8(), InvalidConversion); + BOOST_CHECK_THROW(value.asUint16(), InvalidConversion); + BOOST_CHECK_THROW(value.asInt32(), InvalidConversion); + + value = "true"; + BOOST_CHECK(value.asBool()); + value = "false"; + BOOST_CHECK(!value.asBool()); + value = "1"; + BOOST_CHECK(value.asBool()); + value = "0"; + BOOST_CHECK(!value.asBool()); + value = "other"; + BOOST_CHECK_THROW(value.asBool(), InvalidConversion); +} + +QPID_AUTO_TEST_CASE(testAssignment) +{ + Variant value("abc"); + Variant other = value; + BOOST_CHECK_EQUAL(STRING, value.getType()); + BOOST_CHECK_EQUAL(other.getType(), value.getType()); + BOOST_CHECK_EQUAL(other.asString(), value.asString()); + + const uint32_t i(1000); + value = i; + BOOST_CHECK_EQUAL(UINT32, value.getType()); + BOOST_CHECK_EQUAL(STRING, other.getType()); +} + +QPID_AUTO_TEST_CASE(testList) +{ + const std::string s("abc"); + const float f(9.876); + const int16_t x(1000); + + Variant value = Variant::List(); + value.asList().push_back(Variant(s)); + value.asList().push_back(Variant(f)); + value.asList().push_back(Variant(x)); + BOOST_CHECK_EQUAL(3u, value.asList().size()); + Variant::List::const_iterator i = value.asList().begin(); + + BOOST_CHECK(i != value.asList().end()); + BOOST_CHECK_EQUAL(STRING, i->getType()); + BOOST_CHECK_EQUAL(s, i->asString()); + i++; + + BOOST_CHECK(i != value.asList().end()); + BOOST_CHECK_EQUAL(FLOAT, i->getType()); + BOOST_CHECK_EQUAL(f, i->asFloat()); + i++; + + BOOST_CHECK(i != value.asList().end()); + BOOST_CHECK_EQUAL(INT16, i->getType()); + BOOST_CHECK_EQUAL(x, i->asInt16()); + i++; + + BOOST_CHECK(i == value.asList().end()); +} + +QPID_AUTO_TEST_CASE(testMap) +{ + const std::string red("red"); + const float pi(3.14); + const int16_t x(1000); + + Variant value = Variant::Map(); + value.asMap()["colour"] = red; + value.asMap()["pi"] = pi; + value.asMap()["my-key"] = x; + BOOST_CHECK_EQUAL(3u, value.asMap().size()); + + BOOST_CHECK_EQUAL(STRING, value.asMap()["colour"].getType()); + BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString()); + + BOOST_CHECK_EQUAL(FLOAT, value.asMap()["pi"].getType()); + BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat()); + + BOOST_CHECK_EQUAL(INT16, value.asMap()["my-key"].getType()); + BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16()); + + value.asMap()["my-key"] = "now it's a string"; + BOOST_CHECK_EQUAL(STRING, value.asMap()["my-key"].getType()); + BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString()); +} + +QPID_AUTO_TEST_SUITE_END() |