summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Conway <aconway@apache.org>2008-04-10 12:36:58 +0000
committerAlan Conway <aconway@apache.org>2008-04-10 12:36:58 +0000
commit599ff4a59af78af75639a05a3e87a29f81a29e2b (patch)
treefc7bf12213abb6c2cdbfa8dd658cbc2c62f47741
parent0e16f6997fe87a5241d0cbbd6ad6b0df1bfffc8f (diff)
downloadqpid-python-599ff4a59af78af75639a05a3e87a29f81a29e2b.tar.gz
Invocation handlers, see src/tests/amqp_0_10/handlers.cpp for example.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@646778 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-xcpp/rubygen/0-10/handlers.rb29
-rwxr-xr-xcpp/rubygen/0-10/specification.rb25
-rwxr-xr-xcpp/rubygen/cppgen.rb1
-rw-r--r--cpp/src/qpid/amqp_0_10/Holder.h50
-rw-r--r--cpp/src/qpid/amqp_0_10/Unit.h9
-rw-r--r--cpp/src/tests/Makefile.am3
-rw-r--r--cpp/src/tests/amqp_0_10/handlers.cpp125
7 files changed, 220 insertions, 22 deletions
diff --git a/cpp/rubygen/0-10/handlers.rb b/cpp/rubygen/0-10/handlers.rb
new file mode 100755
index 0000000000..c23eb5faf4
--- /dev/null
+++ b/cpp/rubygen/0-10/handlers.rb
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class GenHandlers < CppGen
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @ns="qpid::amqp_#{@amqp.version.bars}"
+ @dir="qpid/amqp_#{@amqp.version.bars}"
+ end
+
+ def action_handler(type, actions)
+ genl
+ bases=actions.map { |a| "public #{a.fqclassname}::Handler" }
+ struct("#{type}Handler", *bases) { }
+ end
+
+ def generate()
+ h_file("#{@dir}/handlers.h") {
+ include "specification"
+ namespace("#{@ns}") {
+ action_handler "Command", @amqp.collect_all(AmqpCommand)
+ action_handler "Control", @amqp.collect_all(AmqpControl)
+ }
+ }
+ end
+end
+
+GenHandlers.new($outdir, $amqp).generate()
diff --git a/cpp/rubygen/0-10/specification.rb b/cpp/rubygen/0-10/specification.rb
index abc754d1ef..020cb63c89 100755
--- a/cpp/rubygen/0-10/specification.rb
+++ b/cpp/rubygen/0-10/specification.rb
@@ -69,7 +69,7 @@ class Specification < CppGen
genl "bool operator==(const #{x.classname}&, const #{x.classname}&);"
end
- def action_struct_cpp(x)
+ def action_struct_cpp(x, &block)
genl
genl "const char* #{x.classname}::NAME=\"#{x.fqname}\";"
genl "const char* #{x.classname}::CLASS_NAME=#{x.containing_class.nsname}::NAME;"
@@ -92,6 +92,7 @@ class Specification < CppGen
genl "o << \"]\";"
genl "return o;"
}
+ yield if block
end
# structs
@@ -103,13 +104,28 @@ class Specification < CppGen
def action_h(a)
action_struct_h(a, a.base, ["code"]) {
- function_defn("template <class T> void invoke", ["T& target"]) {
- genl "target.#{a.funcname}(#{a.values.join(', ')});"
+ struct("Handler") {
+ scope("void #{a.funcname}(", ");") {
+ genl a.parameters.join(",\n")
+ }
+ }
+ function_defn("template <class T> void invoke", ["T& target"], "const") {
+ genl "target.#{a.funcname}(#{a.values.join(', ')} );"
}
}
end
- def action_cpp(a) action_struct_cpp(a); end
+ def action_cpp(a)
+ action_struct_cpp(a) {
+ scope("void #{a.classname}::Handler::#{a.funcname}(", ")") {
+ genl a.unused_parameters.join(",\n")
+ }
+ scope {
+ genl "assert(0);"
+ genl "throw NotImplementedException(QPID_MSG(\"#{a.fqname} not implemented.\"));"
+ }
+ }
+ end
# Types that must be generated early because they are used by other types.
def pregenerate?(x) not @amqp.used_by[x.fqname].empty?; end
@@ -172,6 +188,7 @@ class Specification < CppGen
cpp_file("#{@dir}/specification") {
include "#{@dir}/specification"
+ include "#{@dir}/exceptions"
include "<iostream>"
# FIXME aconway 2008-03-04: add Struct visitors.
["Command","Control"].each { |x| include "#{@dir}/Apply#{x}" }
diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb
index d0f31a8ba8..0c17b68335 100755
--- a/cpp/rubygen/cppgen.rb
+++ b/cpp/rubygen/cppgen.rb
@@ -152,6 +152,7 @@ end
module AmqpHasFields
def parameters() fields.map { |f| "#{f.paramtype} #{f.cppname}_"} end
+ def unused_parameters() fields.map { |f| "#{f.paramtype} /*#{f.cppname}_*/"} end
def arguments() fields.map { |f| "#{f.cppname}_"} end
def values() fields.map { |f| "#{f.cppname}"} end
def initializers() fields.map { |f| "#{f.cppname}(#{f.cppname}_)"} end
diff --git a/cpp/src/qpid/amqp_0_10/Holder.h b/cpp/src/qpid/amqp_0_10/Holder.h
index 4c1fccd44f..1664afcc8f 100644
--- a/cpp/src/qpid/amqp_0_10/Holder.h
+++ b/cpp/src/qpid/amqp_0_10/Holder.h
@@ -27,29 +27,36 @@
namespace qpid {
namespace amqp_0_10 {
+using framing::in_place;
+
+template <class Invokable> struct InvokeVisitor {
+ typedef void result_type;
+ Invokable& target;
+ InvokeVisitor(Invokable& i) : target(i) {}
+
+ template <class Action>
+ void operator()(const Action& action) { action.invoke(target); }
+};
+
template <class DerivedHolder, class BaseHeld, size_t Size>
-struct Holder : public framing::Blob<Size, BaseHeld> {
+class Holder : public framing::Blob<Size, BaseHeld> {
typedef framing::Blob<Size, BaseHeld> Base;
-
- struct Assign : public ApplyFunctor<void> {
- Holder& holder;
- Assign(Holder& x) : holder(x) {}
- template <class T> void operator()(const T& rhs) { holder=rhs; }
- };
+ public:
+
Holder() {}
- Holder(const BaseHeld& x) { *this=x; }
- template <class T> Holder(const T& value) : Base(value) {}
+ template <class T> explicit Holder(const T& value) : Base(value) {}
using Base::operator=;
- Holder& operator=(const BaseHeld& rhs) {
- Assign assign(*this);
- apply(assign, rhs);
- return *this;
- }
+ Holder& operator=(const BaseHeld& rhs);
uint8_t getCode() const { return this->get()->getCode(); }
uint8_t getClassCode() const { return this->get()->getClassCode(); }
+
+ template <class Invokable> void invoke(Invokable& i) const {
+ InvokeVisitor<Invokable> v(i);
+ apply(v, *this->get());
+ }
template <class S> void encode(S& s) const {
s(getClassCode())(getCode());
@@ -65,8 +72,23 @@ struct Holder : public framing::Blob<Size, BaseHeld> {
s.split(*this);
apply(s, *this->get());
}
+
+ private:
+ struct Assign : public ApplyFunctor<void> {
+ Holder& holder;
+ Assign(Holder& x) : holder(x) {}
+ template <class T> void operator()(const T& rhs) { holder=rhs; }
+ };
};
+template <class D, class B, size_t S>
+Holder<D,B,S>& Holder<D,B,S>::operator=(const B& rhs) {
+ Assign assign(*this);
+ apply(assign, rhs);
+ return *this;
+}
+
+
}} // namespace qpid::amqp_0_10
diff --git a/cpp/src/qpid/amqp_0_10/Unit.h b/cpp/src/qpid/amqp_0_10/Unit.h
index bcba36e35a..0229e07419 100644
--- a/cpp/src/qpid/amqp_0_10/Unit.h
+++ b/cpp/src/qpid/amqp_0_10/Unit.h
@@ -34,15 +34,12 @@
namespace qpid {
namespace amqp_0_10 {
-
/**
* A Unit contains a frame header and associated value.
* For all types except BODY the frame header is for a complete segment.
*/
class Unit {
public:
- typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant;
-
explicit Unit(const FrameHeader& h=FrameHeader()) : header(h) { updateVariant(); }
/**
@@ -57,12 +54,18 @@ class Unit {
template<class T> const T* get() const { return boost::get<T>(&variant); }
template<class T> T* get() { return boost::get<T>(&variant); }
template<class T> Unit& operator=(const T& t) { variant=t; return *this; }
+
+ template <class V> typename V::result_type applyVisitor(V& v) const {
+ variant.apply_visitor(v);
+ }
template <class S> void serialize(S& s) { variant.apply_visitor(s); s.split(*this); }
template <class S> void encode(S&) const {}
template <class S> void decode(S&) { updateHeader(header.getFlags()); }
private:
+ typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant;
+
void updateHeader(uint8_t flags);
void updateVariant();
diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index 3f49d2c6af..7eddd9932e 100644
--- a/cpp/src/tests/Makefile.am
+++ b/cpp/src/tests/Makefile.am
@@ -43,7 +43,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
amqp_0_10/ProxyTemplate.cpp \
amqp_0_10/apply.cpp \
IncompleteMessageList.cpp \
- amqp_0_10/Map.cpp
+ amqp_0_10/Map.cpp \
+ amqp_0_10/handlers.cpp
check_LTLIBRARIES += libshlibtest.la
libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir)
diff --git a/cpp/src/tests/amqp_0_10/handlers.cpp b/cpp/src/tests/amqp_0_10/handlers.cpp
new file mode 100644
index 0000000000..035429a15d
--- /dev/null
+++ b/cpp/src/tests/amqp_0_10/handlers.cpp
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "qpid/Exception.h"
+#include "qpid/amqp_0_10/Unit.h"
+#include "qpid/amqp_0_10/ControlHolder.h"
+#include "qpid/amqp_0_10/CommandHolder.h"
+#include "qpid/amqp_0_10/handlers.h"
+#include "qpid/amqp_0_10/specification.h"
+
+QPID_AUTO_TEST_SUITE(handler_tests)
+
+using namespace qpid::amqp_0_10;
+using namespace std;
+
+string called; // Set by called handler function
+
+// Note on handlers:
+//
+// Control and Command handlers are separate, both behave the same way,
+// so substitute "control or command" for command in the following.
+//
+// Command handlers derive from CommandHandler and implement functions
+// for all the commands they handle. Handling an unimplemented command
+// will raise NotImplementedException.
+//
+// Using virtual inheritance from CommandHandler allows multiple
+// handlers to be aggregated into one with multiple inheritance,
+// See test code for example.
+//
+// E.g. the existing broker model would have two control handlers:
+// - ConnectionHandler: ControlHandler for connection controls.
+// - SessionHandler: ControlHandler for session controls.
+// It would have class-command handlers for each AMQP class:
+// - QueueHandler, MessageHandler etc.. handle each class.
+// And an aggregate handler in place of BrokerAdapter
+// - BrokerCommandHandler: public QueueHandler, MessageHandler ...
+//
+// In other applications (e.g. cluster) any combination of commands
+// can be handled by a given handler. It _might_ simplify the code
+// to collaps ConnectionHandler and SessionHandler into a single
+// ControlHandler (or it might not.)
+
+struct TestExecutionHandler : public virtual CommandHandler {
+ void executionSync() { called = "executionSync"; }
+ // ... etc. for all execution commands
+};
+
+struct TestMessageHandler : public virtual CommandHandler {
+ void messageCancel(const Str8&) { called="messageCancel"; }
+ // ... etc.
+};
+
+// Aggregate handler for all recognised commands.
+struct TestCommandHandler :
+ public TestExecutionHandler,
+ public TestMessageHandler
+ // ... etc. handlers for all command classes.
+{}; // Nothing to do.
+
+
+// Sample unit handler, written as a static_visitor.
+// Note it could equally be written with if/else statements
+// in handle.
+//
+struct TestUnitHandler : public boost::static_visitor<void> {
+ TestCommandHandler handler;
+ void handle(const Unit& u) { u.applyVisitor(*this); }
+
+ void operator()(const Body&) { called="Body"; }
+ void operator()(const Header&) { called="Header"; }
+ void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); }
+ void operator()(const CommandHolder& c) { c.invoke(handler); }
+};
+
+BOOST_AUTO_TEST_CASE(testHandlers) {
+ TestUnitHandler handler;
+ Unit u;
+
+ u = Body();
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("Body", called);
+
+ u = Header();
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("Header", called);
+
+ // in_place<Foo>(...) is equivalent to Foo(...) but
+ // constructs Foo directly in the holder, avoiding
+ // a copy.
+
+ u = CommandHolder(in_place<execution::Sync>());
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("executionSync", called);
+
+ u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array()));
+ try {
+ handler.handle(u);
+ } catch (const qpid::Exception&) {}
+
+ u = CommandHolder(in_place<message::Cancel>(Str8()));
+ handler.handle(u);
+ BOOST_CHECK_EQUAL("messageCancel", called);
+}
+
+QPID_AUTO_TEST_SUITE_END()