qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From acon...@apache.org
Subject [2/2] qpid-proton git commit: PROTON-865: Tutorial and examples, added client.cpp and server_direct.cpp
Date Fri, 07 Aug 2015 20:41:37 GMT
PROTON-865: Tutorial and examples, added client.cpp and server_direct.cpp

Fixed some minor bugs turned up by the examples.

server_direct implemented and in tutorial


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/db11dee5
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/db11dee5
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/db11dee5

Branch: refs/heads/cjansen-cpp-client
Commit: db11dee5af074e3b24f0666ee9ab52cac489d01a
Parents: 2b7f56d
Author: Alan Conway <aconway@redhat.com>
Authored: Fri Aug 7 13:48:34 2015 -0400
Committer: Alan Conway <aconway@redhat.com>
Committed: Fri Aug 7 16:37:37 2015 -0400

----------------------------------------------------------------------
 examples/cpp/CMakeLists.txt                     |   2 +
 examples/cpp/README.hpp                         | 120 ++++++++++++++++
 examples/cpp/README.md                          | 130 -----------------
 examples/cpp/client.cpp                         |  97 +++++++++++++
 examples/cpp/example_test.py                    |  66 ++++-----
 examples/cpp/server.cpp                         |  38 ++---
 examples/cpp/server_direct.cpp                  | 104 ++++++++++++++
 examples/cpp/sync_client.cpp                    |  20 +--
 examples/python/client.py                       |   5 +-
 proton-c/bindings/cpp/CMakeLists.txt            |   2 +-
 proton-c/bindings/cpp/docs/tutorial.hpp         | 138 +++++++++++++------
 proton-c/bindings/cpp/docs/user.doxygen.in      |   2 +-
 .../bindings/cpp/include/proton/message.hpp     |   2 +
 .../cpp/include/proton/proton_handle.hpp        |   7 +-
 proton-c/bindings/cpp/src/message.cpp           |  10 +-
 15 files changed, 494 insertions(+), 249 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt
index f32c38a..1822c6a 100644
--- a/examples/cpp/CMakeLists.txt
+++ b/examples/cpp/CMakeLists.txt
@@ -31,7 +31,9 @@ foreach(example
     direct_recv
     direct_send
     sync_client
+    client
     server
+    server_direct
     encode_decode)
   add_executable(${example} ${example}.cpp)
   target_link_libraries(${example} qpid-proton-cpp)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/README.hpp
----------------------------------------------------------------------
diff --git a/examples/cpp/README.hpp b/examples/cpp/README.hpp
new file mode 100644
index 0000000..d2a51eb
--- /dev/null
+++ b/examples/cpp/README.hpp
@@ -0,0 +1,120 @@
+// Examples overview.
+//
+// For a better overview, see the tutorial in the generated documentation.
+// In your build directory do:
+//     make docs-cpp
+// then open proton-c/bindings/cpp/docs/html/tutorial.html in your browser.
+//
+
+// DEVELOPER NOTE: if you are adding or modifying examples you should keep this
+// file and ../proton-c/bindings/cpp/docs/tutorial.hpp up to date.
+
+/** \example broker.cpp
+
+A very simple "mini broker". You can use this to run other examples that reqiure
+an intermediary, or you can use any AMQP 1.0 broker. This broker creates queues
+automatically when a client tries to send or subscribe.
+
+*/
+
+/** \example helloworld.cpp
+
+Basic example that connects to an intermediary on 127.0.0.1:5672,
+establishes a subscription from the 'examples' nodeu on that
+intermediary, then creates a sending link to the same node and sends
+one message. On receving the message back via the subcription, the
+connection is closed.
+
+*/
+
+/** \example helloworld_direct.cpp
+
+A variant of the basic helloworld example, that does not use an
+intermediary, but listens for incoming connections itself. It
+establishes a connection to itself with a link over which a single
+message is sent. This demonstrates the ease with which a simple daemon
+can be built using the API.
+
+*/
+
+/** \example helloworld_blocking.cpp
+
+The same as the basic helloworld.cpp, but using a
+synchronous/sequential style wrapper on top of the
+asynchronous/reactive API. The purpose of this example is just to show
+how different functionality can be easily layered should it be
+desired.
+
+*/
+
+/** \example simple_send.cpp
+
+An example of sending a fixed number of messages and tracking their
+(asynchronous) acknowledgement. Messages are sent through the 'examples' node on
+an intermediary accessible on 127.0.0.1:5672.
+
+*/
+
+/** \example simple_recv.cpp
+
+Subscribes to the 'examples' node on an intermediary accessible
+on 127.0.0.1:5672. Simply prints out the body of received messages.
+
+*/
+
+/** \example direct_send.cpp
+
+Accepts an incoming connection and then sends like `simple_send`.  You can
+connect directly to `direct_send` *without* a broker using \ref simple_recv.cpp.
+Make sure to stop the broker first or use a different port for `direct_send`.
+
+*/
+
+/** \example direct_recv.cpp
+
+Accepts an incoming connection and then receives like `simple_recv`.  You can
+connect directly to `direct_recv` *without* a broker using \ref simple_send.cpp.
+Make sure to stop the broker first or use a different port for `direct_recv`.
+
+*/
+
+/** \example encode_decode.cpp
+
+Shows how C++ data types can be converted to and from AMQP types.
+
+*/
+
+/** \example client.cpp
+
+The client part of a request-response example. Sends requests and
+prints out responses. Requires an intermediary that supports the AMQP
+1.0 dynamic nodes on which the responses are received. The requests
+are sent through the 'examples' node.
+
+*/
+
+/** \example server.cpp
+
+The server part of a request-response example, that receives requests
+via the examples node, converts the body to uppercase and sends the
+result back to the indicated reply address.
+
+*/
+
+/** \example server_direct.cpp
+
+A variant of the server part of a request-response example, that
+accepts incoming connections and does not need an intermediary. Much
+like the original server, it receives incoming requests, converts the
+body to uppercase and sends the result back to the indicated reply
+address. Can be used in conjunction with any of the client
+alternatives.
+
+*/
+
+/** \example sync_client.cpp
+
+A variant of the client part, that uses a blocking/synchronous style
+instead of the reactive/asynchronous style.
+
+*/

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/README.md
----------------------------------------------------------------------
diff --git a/examples/cpp/README.md b/examples/cpp/README.md
deleted file mode 100644
index bd94b55..0000000
--- a/examples/cpp/README.md
+++ /dev/null
@@ -1,130 +0,0 @@
-C++ examples
-============
-
-Many of the examples expect a broker to be running on the standard AMQP
-port. You can use any broker that supports AMQP 1.0, or you can use the simple
-example `broker` provided here. Run the broker in a separate window before
-running the other examples.
-
-If you use another broker you will need to create a queue named `examples`.
-
-NOTE: Most of these examples are described in more detail as part of the \ref tutorial.
-
-Note on Brokers and URLs
-------------------------
-
-Some of the examples require an AMQP *broker* that can receive, store and send messages.
-
-There is a very simple broker included as an example (\ref broker.cpp) which you can use.
-Run it like this:
-
-    broker [URL]
-
-The default URL is "0.0.0.0:5672" which listens on the standard AMQP port on all
-network interfaces.
-
-Instead of the example broker, you can use any AMQP 1.0 compliant broker. You
-must configure your broker to have a queue or topic named "examples".
-
-Most of the examples take an optional URL argument, the simplest form is:
-
-    HOST:PORT/ADDRESS
-
-It usually defaults to `127.0.0.1:5672/examples`, but you can change this if
-your broker is on a different host or port, or you want to use a different queue
-or topic name (the ADDRESS part of the URL). URL details are at `proton::url`
-
-broker.cpp
-----------
-
-A very simple "mini broker". You can use this to run other examples that reqiure
-an intermediary, or you can use any AMQP 1.0 broker. This broker creates queues
-automatically when a client tries to send or subscribe.
-
-Source: \ref broker.cpp
-
-helloworld.cpp
---------------
-
-Basic example that connects to an intermediary on 127.0.0.1:5672,
-establishes a subscription from the 'examples' nodeu on that
-intermediary, then creates a sending link to the same node and sends
-one message. On receving the message back via the subcription, the
-connection is closed.
-
-Source: \ref helloworld.cpp
-
-helloworld_direct.cpp
----------------------
-
-A variant of the basic helloworld example, that does not use an
-intermediary, but listens for incoming connections itself. It
-establishes a connection to itself with a link over which a single
-message is sent. This demonstrates the ease with which a simple daemon
-can be built using the API.
-
-Source: \ref helloworld_direct.cpp
-
-helloworld_blocking.cpp
------------------------
-
-The same as the basic helloworld.cpp, but using a
-synchronous/sequential style wrapper on top of the
-asynchronous/reactive API. The purpose of this example is just to show
-how different functionality can be easily layered should it be
-desired.
-
-Source: \ref helloworld_blocking.cpp
-
-simple_send.cpp
----------------
-
-An example of sending a fixed number of messages and tracking their
-(asynchronous) acknowledgement. Messages are sent through the 'examples' node on
-an intermediary accessible on 127.0.0.1:5672.
-
-Source: \ref simple_send.cpp
-
-simple_recv.cpp
----------------
-
-Subscribes to the 'examples' node on an intermediary accessible
-on 127.0.0.1:5672. Simply prints out the body of received messages.
-
-Source: \ref simple_recv.cpp
-
-direct_send.cpp
----------------
-
-Accepts an incoming connection and then sends like `simple_send`.  You can
-connect directly to `direct_send` *without* a broker using \ref simple_recv.cpp.
-Make sure to stop the broker first or use a different port for `direct_send`.
-
-Source: \ref direct_send.cpp
-
-direct_recv.cpp
----------------
-
-Accepts an incoming connection and then receives like `simple_recv`.  You can
-connect directly to `direct_recv` *without* a broker using \ref simple_send.cpp.
-Make sure to stop the broker first or use a different port for `direct_recv`.
-
-Source: \ref direct_recv.cpp
-
-encode_decode.cpp
------------------
-
-Shows how C++ data types can be converted to and from AMQP types.
-
-Source: \ref encode_decode.cpp
-
-    <!-- For generated doxygen docmentation. Note \ref tags above are also for doxygen
-->
-    \example helloworld.cpp
-    \example helloworld_direct.cpp
-    \example helloworld_blocking.cpp
-    \example broker.cpp
-    \example encode_decode.cpp
-    \example simple_recv.cpp
-    \example simple_send.cpp
-    \example direct_recv.cpp
-    \example direct_send.cpp

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/client.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp
new file mode 100644
index 0000000..be5fc40
--- /dev/null
+++ b/examples/cpp/client.cpp
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "options.hpp"
+#include "proton/container.hpp"
+#include "proton/messaging_handler.hpp"
+#include "proton/connection.hpp"
+#include <iostream>
+#include <vector>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+class client : public proton::messaging_handler {
+  private:
+    proton::url url;
+    std::vector<std::string> requests;
+    proton::sender sender;
+    proton::receiver receiver;
+
+  public:
+    client(const proton::url &u, const std::vector<std::string>& r) : url(u),
requests(r) {}
+
+    void on_start(proton::event &e) {
+        sender = e.container().create_sender(url);
+        // Create a receiver with a dynamically chosen unique address.
+        receiver = e.container().create_receiver(sender.connection(), "", true/*dynamic*/);
+    }
+
+    void send_request() {
+        proton::message req;
+        req.body(requests.front());
+        req.reply_to(receiver.remote_source().address());
+        sender.send(req);
+    }
+
+    void on_link_opened(proton::event &e) {
+        if (e.link() == receiver) 
+            send_request();
+    }
+
+    void on_message(proton::event &e) {
+        proton::message response = e.message();
+        std::cout << '"' << requests.front() << '"' << " => "
<< response.body() << std::endl;
+        requests.erase(requests.begin());
+        if (!requests.empty()) {
+            send_request();
+        } else {
+            e.connection().close();
+        }
+    }
+};
+
+int main(int argc, char **argv) {
+    // Command line options
+    proton::url url("127.0.0.1:5672/examples");
+    options opts(argc, argv);
+    opts.add_value(url, 'a', "address", "connect and send to URL", "URL");
+
+    try {
+        opts.parse();
+
+        std::vector<std::string> requests;
+        requests.push_back("Twas brillig, and the slithy toves");
+        requests.push_back("Did gire and gymble in the wabe.");
+        requests.push_back("All mimsy were the borogroves,");
+        requests.push_back("And the mome raths outgrabe.");
+
+        client c(url, requests);
+        proton::container(c).run();
+
+        return 0;
+    } catch (const bad_option& e) {
+        std::cout << opts << std::endl << e.what() << std::endl;
+    } catch (const std::exception& e) {
+        std::cerr << e.what() << std::endl;
+    }
+    return 1;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/example_test.py
----------------------------------------------------------------------
diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py
index 38e3c2c..51e732d 100644
--- a/examples/cpp/example_test.py
+++ b/examples/cpp/example_test.py
@@ -102,31 +102,6 @@ class Broker(object):
         except Exception as e:
             raise Exception("Error running %s: %s", cmd, e)
 
-class Server(object):
-    """Run the test server"""
-
-    @classmethod
-    def get(cls, addr):
-        if not hasattr(cls, "_server"):
-            cls._server = Server(addr)
-        return cls._server
-
-    @classmethod
-    def stop(cls):
-        if cls.get(None) and cls._server.process:
-            cls._server.process.kill()
-            cls._server = None
-
-    def __init__(self, addr):
-        self.addr = addr
-        cmd = [exe_name("server"), "-a", self.addr]
-        try:
-            self.process = Popen(cmd, stdout=NULL, stderr=NULL)
-            time.sleep(0.3)
-
-        except Exception as e:
-            raise Exception("Error running %s: %s", cmd, e)
-
 class ExampleTest(unittest.TestCase):
     """Run the examples, verify they behave as expected."""
 
@@ -177,8 +152,13 @@ class ExampleTest(unittest.TestCase):
         send_expect = "direct_send listening on amqp://%s\nall messages confirmed\n" % (addr)
         self.assertEqual(send_expect, verify(send))
 
+    CLIENT_EXPECT=""""Twas brillig, and the slithy toves" => "TWAS BRILLIG, AND THE SLITHY
TOVES"
+"Did gire and gymble in the wabe." => "DID GIRE AND GYMBLE IN THE WABE."
+"All mimsy were the borogroves," => "ALL MIMSY WERE THE BOROGROVES,"
+"And the mome raths outgrabe." => "AND THE MOME RATHS OUTGRABE."
+"""
     def test_simple_recv_send(self):
-        """Start receiver first, then run sender"""
+        # Start receiver first, then run sender"""
         b = Broker.get()
         recv = background("simple_recv", "-a", b.addr)
         self.assertEqual("all messages confirmed\n", execute("simple_send", "-a", b.addr))
@@ -186,20 +166,30 @@ class ExampleTest(unittest.TestCase):
         recv_expect += "".join(['{"sequence"=%s}\n' % (i+1) for i in range(100)])
         self.assertEqual(recv_expect, verify(recv))
 
+    def test_request_response(self):
+        b = Broker.get()
+        server = background("server", "-a", b.addr)
+        try:
+            self.assertEqual(execute("client", "-a", b.addr), self.CLIENT_EXPECT)
+        finally:
+            server.kill()
+
     def test_sync_request_response(self):
-        """Start server first, then run sync_client"""
         b = Broker.get()
-        s = Server.get(b.addr)
-        expect = """
-"Twas brillig, and the slithy toves" => "TWAS BRILLIG, AND THE SLITHY TOVES"
-"Did gire and gymble in the wabe." => "DID GIRE AND GYMBLE IN THE WABE."
-"All mimsy were the borogroves," => "ALL MIMSY WERE THE BOROGROVES,"
-"And the mome raths outgrabe." => "AND THE MOME RATHS OUTGRABE."
-"""
-        sc = "\n"
-        sc += execute("sync_client", "-a", b.addr)
-        self.assertEqual(expect, sc)
-        Server.stop()
+        server = background("server", "-a", b.addr)
+        try:
+            self.assertEqual(execute("sync_client", "-a", b.addr), self.CLIENT_EXPECT)
+        finally:
+            server.kill()
+
+    def test_request_response_direct(self):
+        addr = pick_addr()
+        server = background("server_direct", "-a", addr+"/examples")
+        wait_addr(addr)
+        try:
+            self.assertEqual(execute("client", "-a", addr+"/examples"), self.CLIENT_EXPECT)
+        finally:
+            server.kill()
 
     def test_encode_decode(self):
         expect="""

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/server.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp
index 78b78d3..bad49d3 100644
--- a/examples/cpp/server.cpp
+++ b/examples/cpp/server.cpp
@@ -38,12 +38,12 @@ class server : public proton::messaging_handler {
 
   public:
 
-    server(const proton::url &u) : url(u) {}
+    server(const std::string &u) : url(u) {}
 
     void on_start(proton::event &e) {
         connection = e.container().connect(url);
         e.container().create_receiver(connection, url.path());
-        std::cout << "server listening on " << url << std::endl;
+        std::cout << "server connected to " << url << std::endl;
     }
 
     std::string to_upper(const std::string &s) {
@@ -53,38 +53,28 @@ class server : public proton::messaging_handler {
         return uc;
     }
 
-    // TODO: on_connection_opened() and ANONYMOUS-RELAY
-
     void on_message(proton::event &e) {
-        proton::sender sender;
-        proton::message msg = e.message();
-        std::cout << "Received " << msg.body() << std::endl;
-        std::string sender_id = msg.reply_to();
-        sender_map::iterator it = senders.find(sender_id);
-        if (it == senders.end()) {
-            sender = e.container().create_sender(connection, sender_id);
-            senders[sender_id] = sender;
-        }
-        else {
-            sender = it->second;
-        }
+        std::cout << "Received " << e.message().body() << std::endl;
+        std::string reply_to = e.message().reply_to();
         proton::message reply;
-        reply.body(to_upper(msg.body().get<std::string>()));
-        reply.correlation_id(msg.correlation_id());
-        reply.address(sender_id);
-        sender.send(reply);
+        reply.address(reply_to);
+        reply.body(to_upper(e.message().body().get<std::string>()));
+        reply.correlation_id(e.message().correlation_id());
+        if (!senders[reply_to])
+            senders[reply_to] = e.container().create_sender(connection, reply_to);
+        senders[reply_to].send(reply);
     }
 };
 
 int main(int argc, char **argv) {
     // Command line options
-    proton::url url("amqp://127.0.0.1:5672/examples");
+    std::string address("amqp://127.0.0.1:5672/examples");
     options opts(argc, argv);
-    opts.add_value(url, 'a', "address", "listen on URL", "URL");
+    opts.add_value(address, 'a', "address", "listen on URL", "URL");
     try {
         opts.parse();
-        server server(url);
-        proton::container(server).run();
+        server srv(address);
+        proton::container(srv).run();
         return 0;
     } catch (const bad_option& e) {
         std::cout << opts << std::endl << e.what() << std::endl;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/server_direct.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/server_direct.cpp b/examples/cpp/server_direct.cpp
new file mode 100644
index 0000000..a2571ca
--- /dev/null
+++ b/examples/cpp/server_direct.cpp
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "options.hpp"
+
+#include "proton/container.hpp"
+#include "proton/messaging_handler.hpp"
+#include "proton/url.hpp"
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <sstream>
+
+class server : public proton::messaging_handler {
+  private:
+    typedef std::map<std::string, proton::sender> sender_map;
+    proton::url url;
+    proton::connection connection;
+    sender_map senders;
+    int address_counter;
+
+  public:
+
+    server(const std::string &u) : url(u), address_counter(0) {}
+
+    void on_start(proton::event &e) {
+        e.container().listen(url);
+        std::cout << "server listening on " << url << std::endl;
+    }
+
+    std::string to_upper(const std::string &s) {
+        std::string uc(s);
+        size_t l = uc.size();
+        for (size_t i=0; i<l; i++) uc[i] = std::toupper(uc[i]);
+        return uc;
+    }
+
+    std::string generate_address() {
+        std::ostringstream addr;
+        addr << "server" << address_counter++;
+        return addr.str();
+    }
+
+    void on_link_opening(proton::event& e) {
+        proton::link link = e.link();
+        if (link.is_sender() && link.remote_source() && link.remote_source().is_dynamic())
{
+            link.source().address(generate_address());
+            senders[link.source().address()] = link;
+        }
+    }
+
+    void on_message(proton::event &e) {
+        std::cout << "Received " << e.message().body() << std::endl;
+        std::string reply_to = e.message().reply_to();
+        sender_map::iterator it = senders.find(reply_to);
+        if (it == senders.end()) {
+            std::cout << "No link for reply_to: " << reply_to << std::endl;
+        } else {
+            proton::sender sender = it->second;
+            proton::message reply;
+            reply.address(reply_to);
+            reply.body(to_upper(e.message().body().get<std::string>()));
+            reply.correlation_id(e.message().correlation_id());
+            sender.send(reply);
+        }
+    }
+};
+
+int main(int argc, char **argv) {
+    // Command line options
+    std::string address("amqp://127.0.0.1:5672/examples");
+    options opts(argc, argv);
+    opts.add_value(address, 'a', "address", "listen on URL", "URL");
+    try {
+        opts.parse();
+        server srv(address);
+        proton::container(srv).run();
+        return 0;
+    } catch (const bad_option& e) {
+        std::cout << opts << std::endl << e.what() << std::endl;
+    } catch (const std::exception& e) {
+        std::cerr << e.what() << std::endl;
+    }
+    return 1;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/cpp/sync_client.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/sync_client.cpp b/examples/cpp/sync_client.cpp
index e141a3e..d0ad6fc 100644
--- a/examples/cpp/sync_client.cpp
+++ b/examples/cpp/sync_client.cpp
@@ -28,8 +28,10 @@
 #include "proton/types.hpp"
 
 #include <iostream>
+#include <vector>
 #include <string>
 
+
 int main(int argc, char **argv) {
     // Command line options
     proton::url url("127.0.0.1:5672/examples");
@@ -38,20 +40,20 @@ int main(int argc, char **argv) {
     opts.add_value(url, 'a', "address", "connect to URL", "URL");
     opts.add_value(timeout, 't', "timeout", "give up after this TIMEOUT (milliseconds)",
"TIMEOUT");
 
-    std::string requests[] = { "Twas brillig, and the slithy toves",
-                               "Did gire and gymble in the wabe.",
-                               "All mimsy were the borogroves,",
-                               "And the mome raths outgrabe." };
-    int requests_size=4;
+    std::vector<std::string> requests;
+    requests.push_back("Twas brillig, and the slithy toves");
+    requests.push_back("Did gire and gymble in the wabe.");
+    requests.push_back("All mimsy were the borogroves,");
+    requests.push_back("And the mome raths outgrabe.");
 
     try {
         opts.parse();
-        proton::duration d(timeout);
-        proton::blocking_connection conn(url, d);
+
+        proton::blocking_connection conn(url, proton::duration(timeout));
         proton::sync_request_response client(conn, url.path());
-        for (int i=0; i<requests_size; i++) {
+        for (std::vector<std::string>::const_iterator i=requests.begin(); i != requests.end();
i++) {
             proton::message request;
-            request.body(requests[i]);
+            request.body(*i);
             proton::message response = client.call(request);
             std::cout << request.body() << " => " << response.body()
<< std::endl;
         }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/examples/python/client.py
----------------------------------------------------------------------
diff --git a/examples/python/client.py b/examples/python/client.py
index 18dc81a..b57e25b 100755
--- a/examples/python/client.py
+++ b/examples/python/client.py
@@ -35,9 +35,8 @@ class Client(MessagingHandler):
         self.receiver = event.container.create_receiver(self.sender.connection, None, dynamic=True)
 
     def next_request(self):
-        if self.receiver.remote_source.address:
-            req = Message(reply_to=self.receiver.remote_source.address, body=self.requests[0])
-            self.sender.send(req)
+        req = Message(reply_to=self.receiver.remote_source.address, body=self.requests[0])
+        self.sender.send(req)
 
     def on_link_opened(self, event):
         if event.receiver == self.receiver:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/proton-c/bindings/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt
index d1b1ebc..519eb4e 100644
--- a/proton-c/bindings/cpp/CMakeLists.txt
+++ b/proton-c/bindings/cpp/CMakeLists.txt
@@ -108,7 +108,7 @@ macro(add_cpp_test test)
       "PATH=$<TARGET_FILE_DIR:qpid-proton>"
       $<TARGET_FILE:${test}> ${ARGN})
   else ()
-    add_test (NAME cpp_${test} COMMAND ${memcheck-cmd} ${test} ${ARGN})
+    add_test (NAME cpp_${test} COMMAND ${memcheck-cmd} ${CMAKE_CURRENT_BINARY_DIR}/${test}
${ARGN})
   endif ()
 endmacro(add_cpp_test)
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/proton-c/bindings/cpp/docs/tutorial.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/docs/tutorial.hpp b/proton-c/bindings/cpp/docs/tutorial.hpp
index 337ed21..51fe144 100644
--- a/proton-c/bindings/cpp/docs/tutorial.hpp
+++ b/proton-c/bindings/cpp/docs/tutorial.hpp
@@ -12,17 +12,17 @@ messaging applications in incremental steps. There are further examples,
in
 addition the ones mentioned in the tutorial.
 
 Some of the examples require an AMQP *broker* that can receive, store and send
-messages. \ref broker.cpp is a simple example broker which you can use. When run
-without arguments it listens on `0.0.0.0:5672`, the standard AMQP port on all
-network interfaces. To use a different port or network interface:
+messages. \ref broker.cpp is a simple example broker. Run without arguments it
+listens on `0.0.0.0:5672`, the standard AMQP port on all network interfaces. To
+use a different port or network interface:
 
     broker -a <host>:<port>
 
 Instead of the example broker, you can use any AMQP 1.0 compliant broker. You
 must configure your broker to have a queue (or topic) named "examples".
 
-Most of the examples take an optional URL argument or `-a URL` option the
-URL looks like:
+The `helloworld` examples take an optional URL argument. The other examples take
+an option `-a URL`. A URL looks like:
 
     HOST:PORT/ADDRESS
 
@@ -41,10 +41,8 @@ receiving. In a realistic system the sender and receiver would normally
be in
 different processes. The complete example is \ref helloworld.cpp
 
 We will include the following classes: `proton::container` runs an event loop
-which dispatches events to a `proton::messaging_handler`. This allows a
-*reactive* style of programming which is well suited to messaging
-applications. `proton::url` is a simple parser for the URL format described
-above.
+which dispatches events to a `proton::messaging_handler`. This allows a *reactive*
+style of programming which is well suited to messaging applications. `proton::url` is a simple
parser for the URL format mentioned above.
 
 \skip   proton/container
 \until  proton/url
@@ -301,55 +299,117 @@ as a simple broker for testing purposes is an example of this).
 Request/Response
 ----------------
 
-\todo TODO missing example in C++
-
 A common pattern is to send a request message and expect a response message in
 return. AMQP has special support for this pattern. Let's have a look at a simple
 example. We'll start with \ref server.cpp, the program that will process the
 request and send the response. Note that we are still using a broker in this
 example.
 
-\todo TODO insert server snips
-
 Our server will provide a very simple service: it will respond with the
 body of the request converted to uppercase.
 
+\dontinclude server.cpp
+\skip class server
+\until };
+
 The code here is not too different from the simple receiver example.  When we
-receive a request however, we look at the proton::message::reply_to address on
-proton::message and create a sender for that over which to send the
-response. We'll cache the senders incase we get further requests with the same
-reply\_to.
+receive a request in `on_message` however, we look at the
+`proton::message::reply_to` address and create a sender with that address for
+the response. We'll cache the senders incase we get further requests with the
+same `reply_to`.
 
 Now let's create a simple \ref client.cpp to test this service out.
 
-\todo TODO insert client snips
+\dontinclude client.cpp
+
+Our client takes a list of strings to send as requests
+
+\skipline client(
+
+Since we will be sending and receiving, we create a sender and a receiver in
+`on_start`.  Our receiver has a blank address and sets the `dynamic` flag to
+true, which means we expect the remote end (broker or server) to assign a unique
+address for us.
 
-As well as sending requests, we need to be able to get back the
-responses. We create a receiver for that (see line 14), but we don't
-specify an address, we set the dynamic option which tells the broker we
-are connected to to create a temporary address over which we can receive
-our responses.
+\skip on_start
+\until }
+
+Now a function to send the next request from our list of requests. We set the
+reply_to address to be the dynamically assigned address of our receiver.
+
+\skip send_request
+\until }
 
-We need to use the address allocated by the broker as the reply\_to
-address of our requests, so we can't send them until the broker has
-confirmed our receiving link has been set up (at which point we will
-have our allocated address). To do that, we add an `on_link_opened()`
-method to our handler class, and if the link associated with event is
-the receiver, we use that as the trigger to send our first request.
+We need to use the address assigned by the broker as the `reply_to` address of
+our requests, so we can't send them until our receiver has been set up. To do
+that, we add an `on_link_opened()` method to our handler class, and if the link
+associated with event is the receiver, we use that as the trigger to send our
+first request.
+
+\skip on_link_opened
+\until }
+
+When we receive a reply, we send the next request.
+
+\skip on_message
+\until }
+\until }
+\until }
+
+Direct Request/Response
+-----------------------
 
-Again, we could avoid having any intermediary process here if we wished.
-The following code implementas a server to which the client above could
-connect directly without any need for a broker or similar.
+We can avoid the intermediary process by writing a server that accepts
+connections directly, \ref server_direct.cpp. It involves the following changes
+to our original server:
 
-\todo TODO missing server_direct.cpp
+\dontinclude server_direct.cpp
 
-Though this requires some more extensive changes than the simple sending
-and receiving examples, the essence of the program is still the same.
-Here though, rather than the server establishing a link for the
-response, it relies on the link that the client established, since that
-now comes in directly to the server process.
+First the server must generate a unique reply-to addreses for links from the
+client that request a dynamic address (previously this was done by the broker.)
+We use a simple counter.
+
+\skip generate_address
+\until }
+
+Next we need to handle incoming requests for links with dynamic addresses from
+the client.  We give the link a unique address and record it in our `senders`
+map.
+
+\skip on_link_opening
+\until }
+
+Note we are interested in *sender* links above because we are implementing the
+server. A *receiver* link created on the client corresponds to a *sender* link
+on the server.
+
+Finally when we receive a message we look up its `reply_to` in our senders map and send the
reply.
+
+\skip on_message
+\until }
+\until }
+\until }
+
+Synchronous Request/Response
+----------------------------
+
+The event-driven style of programming is extremely powerful, especially for
+server or mixed client-server programs. However for simple client-only programs
+a synchronous or blocking style of programming is sometimes simpler.
+
+`proton::blocking_connection` allows a blocking style of programming,
+`proton::sync_request_response` automates the common case of synchronous
+request/response, send a request and block for the response.
+
+\ref sync_client.cpp is our request/response client in blocking style. Here's the key section
+
+\dontinclude sync_client.cpp
+\skip conn(
+\until }
+
+*/
 
-### Miscellaneous
+/* TODO selector and browser
 
 Many brokers offer the ability to consume messages based on a 'selector'
 that defines which messages are of interest based on particular values

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/proton-c/bindings/cpp/docs/user.doxygen.in
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/docs/user.doxygen.in b/proton-c/bindings/cpp/docs/user.doxygen.in
index 93252d1..1d2eaf7 100644
--- a/proton-c/bindings/cpp/docs/user.doxygen.in
+++ b/proton-c/bindings/cpp/docs/user.doxygen.in
@@ -616,7 +616,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT = @CMAKE_SOURCE_DIR@/proton-c/bindings/cpp/include @CMAKE_SOURCE_DIR@/proton-c/bindings/cpp/docs
@CMAKE_SOURCE_DIR@/examples/cpp/README.md
+INPUT = @CMAKE_SOURCE_DIR@/proton-c/bindings/cpp/include @CMAKE_SOURCE_DIR@/proton-c/bindings/cpp/docs
@CMAKE_SOURCE_DIR@/examples/cpp/README.hpp
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/proton-c/bindings/cpp/include/proton/message.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/message.hpp b/proton-c/bindings/cpp/include/proton/message.hpp
index 528088b..727d8a5 100644
--- a/proton-c/bindings/cpp/include/proton/message.hpp
+++ b/proton-c/bindings/cpp/include/proton/message.hpp
@@ -32,6 +32,8 @@ struct pn_data_t;
 
 namespace proton {
 
+// TODO aconway 2015-08-07: make this a value-semantics class, hide pn_message_t.
+
 /// An AMQP message.
 class message : public proton_handle<pn_message_t>
 {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/proton-c/bindings/cpp/include/proton/proton_handle.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/proton_handle.hpp b/proton-c/bindings/cpp/include/proton/proton_handle.hpp
index 6fc9211..19ee90b 100644
--- a/proton-c/bindings/cpp/include/proton/proton_handle.hpp
+++ b/proton-c/bindings/cpp/include/proton/proton_handle.hpp
@@ -35,7 +35,6 @@ template <class> class proton_impl_ref;
  */
 template <class T> class proton_handle {
   public:
-
     /**@return true if handle is valid,  i.e. not null. */
     bool is_valid() const { return impl_; }
 
@@ -50,8 +49,14 @@ template <class T> class proton_handle {
 
     void swap(proton_handle<T>& h) { T* t = h.impl_; h.impl_ = impl_; impl_ = t;
}
 
+    bool operator==(const proton_handle<T>& x) { return x.impl_ == impl_; }
+    bool operator!=(const proton_handle<T>& x) { return x.impl_ != impl_; }
+
+    T* raw() { return impl_; }
+
   private:
     // Not implemented, subclasses must implement.
+    // FIXME aconway 2015-08-07: why?
     proton_handle(const proton_handle&);
     proton_handle& operator=(const proton_handle&);
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/db11dee5/proton-c/bindings/cpp/src/message.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/message.cpp b/proton-c/bindings/cpp/src/message.cpp
index 616f23d..e1bd535 100644
--- a/proton-c/bindings/cpp/src/message.cpp
+++ b/proton-c/bindings/cpp/src/message.cpp
@@ -73,9 +73,13 @@ void set_value(pn_data_t* d, const value& v) {
 }
 
 value get_value(pn_data_t* d) {
-    values values(d);
-    values.rewind();
-    return values.get<value>();
+    if (d) {
+        values vals(d);
+        vals.rewind();
+        if (vals.more())
+            return vals.get<value>();
+    }
+    return value();
 }
 } // namespace
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


Mime
View raw message