You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2015/09/04 21:14:33 UTC
[6/7] qpid-proton git commit: PROTON-865: C++ reactor binding for
proton.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/examples/go/event/broker.go
----------------------------------------------------------------------
diff --git a/examples/go/event/broker.go b/examples/go/event/broker.go
deleted file mode 100644
index 9720843..0000000
--- a/examples/go/event/broker.go
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
-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.
-*/
-
-//
-// This is a simple AMQP broker implemented using the event-handler interface.
-//
-// It maintains a set of named in-memory queues of messages. Clients can send
-// messages to queues or subscribe to receive messages from them.
-//
-//
-
-package main
-
-import (
- "container/list"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net"
- "os"
- "path"
- "qpid.apache.org/proton"
- "qpid.apache.org/proton/event"
- "sync"
-)
-
-// Command-line flags
-var addr = flag.String("addr", ":amqp", "Listening address")
-var verbose = flag.Int("verbose", 1, "Output level, 0 means none, higher means more")
-var full = flag.Bool("full", false, "Print full message not just body.")
-
-func main() {
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, `
-Usage: %s
-A simple broker-like demo. Queues are created automatically for sender or receiver addrsses.
-`, os.Args[0])
- flag.PrintDefaults()
- }
- flag.Parse()
- b := newBroker()
- err := b.listen(*addr)
- fatalIf(err)
-}
-
-// queue is a structure representing a queue.
-type queue struct {
- name string // Name of queue
- messages *list.List // List of event.Message
- consumers map[event.Link]bool // Set of consumer links
-}
-
-type logLink event.Link // Wrapper to print links in format for logging
-
-func (ll logLink) String() string {
- l := event.Link(ll)
- return fmt.Sprintf("%s[%p]", l.Name(), l.Session().Connection().Pump())
-}
-
-func (q *queue) subscribe(link event.Link) {
- debug.Printf("link %s subscribed to queue %s", logLink(link), q.name)
- q.consumers[link] = true
-}
-
-func (q *queue) unsubscribe(link event.Link) {
- debug.Printf("link %s unsubscribed from queue %s", logLink(link), q.name)
- delete(q.consumers, link)
-}
-
-func (q *queue) empty() bool {
- return len(q.consumers) == 0 && q.messages.Len() == 0
-}
-
-func (q *queue) push(context *event.Pump, message proton.Message) {
- q.messages.PushBack(message)
- q.pop(context)
-}
-
-func (q *queue) popTo(context *event.Pump, link event.Link) bool {
- if q.messages.Len() != 0 && link.Credit() > 0 {
- message := q.messages.Remove(q.messages.Front()).(proton.Message)
- debug.Printf("link %s <- queue %s: %s", logLink(link), q.name, formatMessage{message})
- // The first return parameter is an event.Delivery.
- // The Deliver can be used to track message status, e.g. so we can re-delver on failure.
- // This demo broker doesn't do that.
- linkPump := link.Session().Connection().Pump()
- if context == linkPump {
- if context == nil {
- log.Fatal("pop in nil context")
- }
- link.Send(message) // link is in the current pump, safe to call Send() direct
- } else {
- linkPump.Inject <- func() { // Inject to link's pump
- link.Send(message) // FIXME aconway 2015-05-04: error handlig
- }
- }
- return true
- }
- return false
-}
-
-func (q *queue) pop(context *event.Pump) (popped bool) {
- for c, _ := range q.consumers {
- popped = popped || q.popTo(context, c)
- }
- return
-}
-
-// broker implements event.MessagingHandler and reacts to events by moving messages on or off queues.
-type broker struct {
- queues map[string]*queue
- lock sync.Mutex // FIXME aconway 2015-05-04: un-golike, better broker coming...
-}
-
-func newBroker() *broker {
- return &broker{queues: make(map[string]*queue)}
-}
-
-func (b *broker) getQueue(name string) *queue {
- q := b.queues[name]
- if q == nil {
- debug.Printf("Create queue %s", name)
- q = &queue{name, list.New(), make(map[event.Link]bool)}
- b.queues[name] = q
- }
- return q
-}
-
-func (b *broker) unsubscribe(l event.Link) {
- if l.IsSender() {
- q := b.queues[l.RemoteSource().Address()]
- if q != nil {
- q.unsubscribe(l)
- if q.empty() {
- debug.Printf("Delete queue %s", q.name)
- delete(b.queues, q.name)
- }
- }
- }
-}
-
-func (b *broker) HandleMessagingEvent(t event.MessagingEventType, e event.Event) error {
- // FIXME aconway 2015-05-04: locking is un-golike, better example coming soon.
- // Needed because the same handler is used for multiple connections concurrently
- // and the queue data structures are not thread safe.
- b.lock.Lock()
- defer b.lock.Unlock()
-
- switch t {
-
- case event.MLinkOpening:
- if e.Link().IsSender() {
- q := b.getQueue(e.Link().RemoteSource().Address())
- q.subscribe(e.Link())
- }
-
- case event.MLinkDisconnected, event.MLinkClosing:
- b.unsubscribe(e.Link())
-
- case event.MSendable:
- q := b.getQueue(e.Link().RemoteSource().Address())
- q.popTo(e.Connection().Pump(), e.Link())
-
- case event.MMessage:
- m, err := event.DecodeMessage(e)
- fatalIf(err)
- qname := e.Link().RemoteTarget().Address()
- debug.Printf("link %s -> queue %s: %s", logLink(e.Link()), qname, formatMessage{m})
- b.getQueue(qname).push(e.Connection().Pump(), m)
- }
- return nil
-}
-
-func (b *broker) listen(addr string) (err error) {
- // Use the standard Go "net" package to listen for connections.
- info.Printf("Listening on %s", addr)
- listener, err := net.Listen("tcp", addr)
- if err != nil {
- return err
- }
- defer listener.Close()
- for {
- conn, err := listener.Accept()
- if err != nil {
- info.Printf("Accept error: %s", err)
- continue
- }
- pump, err := event.NewPump(conn, event.NewMessagingDelegator(b))
- fatalIf(err)
- info.Printf("Accepted %s[%p]", pump, pump)
- pump.Server()
- go func() {
- pump.Run()
- if pump.Error == nil {
- info.Printf("Closed %s", pump)
- } else {
- info.Printf("Closed %s: %s", pump, pump.Error)
- }
- }()
- }
-}
-
-// Logging
-func logger(prefix string, level int, w io.Writer) *log.Logger {
- if *verbose >= level {
- return log.New(w, prefix, 0)
- }
- return log.New(ioutil.Discard, "", 0)
-}
-
-var info, debug *log.Logger
-
-func init() {
- flag.Parse()
- name := path.Base(os.Args[0])
- log.SetFlags(0)
- log.SetPrefix(fmt.Sprintf("%s: ", name)) // Log errors on stderr.
- info = logger(fmt.Sprintf("%s: ", name), 1, os.Stdout) // Log info on stdout.
- debug = logger(fmt.Sprintf("%s debug: ", name), 2, os.Stderr) // Log debug on stderr.
-}
-
-// Simple error handling for demo.
-func fatalIf(err error) {
- if err != nil {
- log.Fatal(err)
- }
-}
-
-type formatMessage struct{ m proton.Message }
-
-func (fm formatMessage) String() string {
- if *full {
- return fmt.Sprintf("%#v", fm.m)
- } else {
- return fmt.Sprintf("%#v", fm.m.Body())
- }
-}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/examples/go/receive.go
----------------------------------------------------------------------
diff --git a/examples/go/receive.go b/examples/go/receive.go
deleted file mode 100644
index fc1c85a..0000000
--- a/examples/go/receive.go
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
-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.
-*/
-
-package main
-
-import (
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math"
- "net"
- "os"
- "path"
- "qpid.apache.org/proton"
- "qpid.apache.org/proton/messaging"
- "sync"
- "time"
-)
-
-// Command-line flags
-var verbose = flag.Int("verbose", 1, "Output level, 0 means none, higher means more")
-var count = flag.Int64("count", 0, "Stop after receiving this many messages. 0 means unlimited.")
-var timeout = flag.Int64("time", 0, "Stop after this many seconds. 0 means unlimited.")
-var full = flag.Bool("full", false, "Print full message not just body.")
-
-func main() {
- // Parse flags and arguments, print usage message on error.
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, `
-Usage: %s url [url ...]
-Receive messages from all the listed URLs concurrently and print them.
-`, os.Args[0])
- flag.PrintDefaults()
- }
- flag.Parse()
- urls := flag.Args() // Non-flag arguments are URLs to receive from
- if len(urls) == 0 {
- flag.Usage()
- fmt.Fprintf(os.Stderr, "No URL provided")
- os.Exit(1)
- }
- duration := time.Duration(*timeout) * time.Second
- if duration == 0 {
- duration = time.Duration(math.MaxInt64) // Not forever, but 290 years is close enough.
- }
- if *count == 0 {
- *count = math.MaxInt64
- }
-
- // Create a goroutine for each URL that receives messages and sends them to
- // the messages channel. main() receives and prints them.
-
- messages := make(chan proton.Message) // Channel for messages from goroutines to main()
- stop := make(chan struct{}) // Closing this channel means the program is stopping.
-
- var wait sync.WaitGroup // Used by main() to wait for all goroutines to end.
-
- wait.Add(len(urls)) // Wait for one goroutine per URL.
-
- // Arrange to close all connections on exit
- connections := make([]*messaging.Connection, len(urls))
- defer func() {
- for _, c := range connections {
- if c != nil {
- c.Close()
- }
- }
- }()
-
- for i, urlStr := range urls {
- debug.Printf("Connecting to %s", urlStr)
- go func(urlStr string) {
- defer wait.Done() // Notify main() that this goroutine is done.
- url, err := proton.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults.
- fatalIf(err)
-
- // Open a standard Go net.Conn for the AMQP connection
- conn, err := net.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port"
- fatalIf(err)
-
- pc, err := messaging.Connect(conn) // This is our AMQP connection.
- fatalIf(err)
- connections[i] = pc
-
- // For convenience a proton.Connection provides a DefaultSession()
- // pc.Receiver() is equivalent to pc.DefaultSession().Receiver()
- r, err := pc.Receiver(url.Path)
- fatalIf(err)
-
- for {
- var m proton.Message
- select { // Receive a message or stop.
- case m = <-r.Receive:
- case <-stop: // The program is stopping.
- return
- }
- select { // Send m to main() or stop
- case messages <- m: // Send m to main()
- case <-stop: // The program is stopping.
- return
- }
- }
- }(urlStr)
- }
- info.Printf("Listening")
-
- // time.After() returns a channel that will close when the timeout is up.
- timer := time.After(duration)
-
- // main() prints each message and checks for count or timeout being exceeded.
- for i := int64(0); i < *count; i++ {
- select {
- case m := <-messages:
- debug.Print(formatMessage{m})
- case <-timer: // Timeout has expired
- i = 0
- }
- }
- info.Printf("Received %d messages", *count)
- close(stop) // Signal all goroutines to stop.
- wait.Wait() // Wait for all goroutines to finish.
-}
-
-// Logging
-func logger(prefix string, level int, w io.Writer) *log.Logger {
- if *verbose >= level {
- return log.New(w, prefix, 0)
- }
- return log.New(ioutil.Discard, "", 0)
-}
-
-var info, debug *log.Logger
-
-func init() {
- flag.Parse()
- name := path.Base(os.Args[0])
- log.SetFlags(0) // Use default logger for errors.
- log.SetPrefix(fmt.Sprintf("%s: ", name)) // Log errors on stderr.
- info = logger(fmt.Sprintf("%s: ", name), 1, os.Stdout) // Log info on stdout.
- debug = logger(fmt.Sprintf("%s debug: ", name), 2, os.Stderr) // Log debug on stderr.
-}
-
-// Simple error handling for demo.
-func fatalIf(err error) {
- if err != nil {
- log.Fatal(err)
- }
-}
-
-type formatMessage struct{ m proton.Message }
-
-func (fm formatMessage) String() string {
- if *full {
- return fmt.Sprintf("%#v", fm.m)
- } else {
- return fmt.Sprintf("%#v", fm.m.Body())
- }
-}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/examples/go/send.go
----------------------------------------------------------------------
diff --git a/examples/go/send.go b/examples/go/send.go
deleted file mode 100644
index 46603bf..0000000
--- a/examples/go/send.go
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
-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.
-*/
-
-package main
-
-import (
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math"
- "net"
- "os"
- "path"
- "qpid.apache.org/proton"
- "qpid.apache.org/proton/messaging"
- "sync"
-)
-
-// Command-line flags
-var verbose = flag.Int("verbose", 1, "Output level, 0 means none, higher means more")
-var count = flag.Int64("count", 1, "Send this may messages per address. 0 means unlimited.")
-
-// Ack associates an info string with an acknowledgement
-type Ack struct {
- ack messaging.Acknowledgement
- info string
-}
-
-func main() {
- // Parse flags and arguments, print usage message on error.
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, `
-Usage: %s url [url ...]
-Send messages to all the listed URLs concurrently.
-To each URL, send the string "path-n" where n is the message number.
-`, os.Args[0])
- flag.PrintDefaults()
- }
- flag.Parse()
- urls := flag.Args() // Non-flag arguments are URLs to receive from
- if len(urls) == 0 {
- flag.Usage()
- fmt.Fprintf(os.Stderr, "No URL provided\n")
- os.Exit(1)
- }
- if *count == 0 {
- *count = math.MaxInt64
- }
-
- // Create a channel to receive all the acknowledgements
- acks := make(chan Ack)
-
- // Create a goroutine for each URL that sends messages.
- var wait sync.WaitGroup // Used by main() to wait for all goroutines to end.
- wait.Add(len(urls)) // Wait for one goroutine per URL.
-
- // Arrange to close all connections on exit
- connections := make([]*messaging.Connection, len(urls))
- defer func() {
- for _, c := range connections {
- c.Close()
- }
- }()
-
- for i, urlStr := range urls {
- url, err := proton.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults.
- fatalIf(err)
- debug.Printf("Connecting to %v", url)
-
- // Open a standard Go net.Conn for the AMQP connection
- conn, err := net.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port"
- fatalIf(err)
-
- pc, err := messaging.Connect(conn) // This is our AMQP connection using conn.
- fatalIf(err)
- connections[i] = pc
-
- // Start a goroutine to send to urlStr
- go func(urlStr string) {
- defer wait.Done() // Notify main() that this goroutine is done.
-
- // FIXME aconway 2015-04-29: sessions, default sessions, senders...
- // Create a sender using the path of the URL as the AMQP target address
- s, err := pc.Sender(url.Path)
- fatalIf(err)
-
- for i := int64(0); i < *count; i++ {
- m := proton.NewMessage()
- body := fmt.Sprintf("%v-%v", url.Path, i)
- m.SetBody(body)
- ack, err := s.Send(m)
- fatalIf(err)
- acks <- Ack{ack, body}
- }
- }(urlStr)
- }
-
- // Wait for all the acknowledgements
- expect := int(*count) * len(urls)
- debug.Printf("Started senders, expect %v acknowledgements", expect)
- for i := 0; i < expect; i++ {
- ack, ok := <-acks
- if !ok {
- info.Fatalf("acks channel closed after only %d acks\n", i)
- }
- d := <-ack.ack
- debug.Printf("acknowledgement[%v] %v", i, ack.info)
- if d != messaging.Accepted {
- info.Printf("Unexpected disposition %v", d)
- }
- }
- info.Printf("Received all %v acknowledgements", expect)
- wait.Wait() // Wait for all goroutines to finish.
-}
-
-// Logging
-func logger(prefix string, level int, w io.Writer) *log.Logger {
- if *verbose >= level {
- return log.New(w, prefix, 0)
- }
- return log.New(ioutil.Discard, "", 0)
-}
-
-var info, debug *log.Logger
-
-func init() {
- flag.Parse()
- name := path.Base(os.Args[0])
- log.SetFlags(0) // Use default logger for errors.
- log.SetPrefix(fmt.Sprintf("%s: ", name)) // Log errors on stderr.
- info = logger(fmt.Sprintf("%s: ", name), 1, os.Stdout) // Log info on stdout.
- debug = logger(fmt.Sprintf("%s debug: ", name), 2, os.Stderr) // Log debug on stderr.
-}
-
-// Simple error handling for demo.
-func fatalIf(err error) {
- if err != nil {
- log.Fatal(err)
- }
-}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index 46cf60e..9bcaa6e 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -217,6 +217,8 @@ if (CMAKE_COMPILER_IS_GNUCC)
set (WERROR "-Werror")
endif (ENABLE_WARNING_ERROR)
set (COMPILE_WARNING_FLAGS "${WERROR} -Wall -pedantic-errors")
+ # C++ allow "%z" format specifier and variadic macros
+ set (CXX_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wno-format -Wno-variadic-macros" CACHE STRING "C++ warning flags")
if (NOT BUILD_WITH_CXX)
set (COMPILE_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wstrict-prototypes")
set (COMPILE_LANGUAGE_FLAGS "-std=c99")
@@ -231,8 +233,7 @@ if (CMAKE_COMPILER_IS_GNUCC)
set (COMPILE_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wc++-compat -Wvla -Wsign-compare -Wwrite-strings")
endif (${GCC_VERSION} VERSION_LESS "4.3.0")
else (NOT BUILD_WITH_CXX)
- # allow "%z" format specifier and variadic macros
- set (COMPILE_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wno-format -Wno-variadic-macros")
+ set (COMPILE_WARNING_FLAGS "${CXX_WARNING_FLAGS}")
endif (NOT BUILD_WITH_CXX)
if (ENABLE_UNDEFINED_ERROR)
@@ -262,10 +263,7 @@ macro (pn_absolute_install_dir NAME VALUE PREFIX)
get_filename_component(${NAME} ${${NAME}} ABSOLUTE)
endmacro()
-if (SWIG_FOUND)
- add_subdirectory(bindings)
-endif (SWIG_FOUND)
-
+add_subdirectory(bindings)
add_subdirectory(docs/api)
add_subdirectory(docs/man)
add_subdirectory(../tests/tools/apps/c ../tests/tools/apps/c)
@@ -448,20 +446,6 @@ install (FILES
${CMAKE_CURRENT_BINARY_DIR}/ProtonConfigVersion.cmake
DESTINATION ${LIB_INSTALL_DIR}/cmake/Proton)
-# CTest
-
-find_program(VALGRIND_EXE valgrind DOC "Location of the valgrind program")
-option(ENABLE_VALGRIND "Use valgrind to detect run-time problems" ON)
-if (ENABLE_VALGRIND)
- if (NOT VALGRIND_EXE)
- message(STATUS "Can't locate the valgrind command; no run-time error detection")
- else ()
- set (VALGRIND_ENV "VALGRIND=${VALGRIND_EXE}")
- endif ()
-endif (ENABLE_VALGRIND)
-
-mark_as_advanced (VALGRIND_EXE)
-
# c tests:
add_subdirectory(src/tests)
@@ -521,28 +505,28 @@ if (BUILD_PYTHON)
# platforms will be supported. Since `setup.py` will skip
# the build for non linux plaforms, it doesn't make sense
# to try to run them.
- if (CMAKE_SYSTEM_NAME STREQUAL Linux)
- find_program(TOX_EXE "tox")
- mark_as_advanced(TOX_EXE)
- if (TOX_EXE)
- to_native_path ("${py_path}" py_path)
- add_test (NAME python-tox-test
- WORKING_DIRECTORY ${py_src}
- COMMAND ${env_py}
- "PATH=${py_path}" "QPID_PROTON_SRC=${CMAKE_CURRENT_SOURCE_DIR}/../"
- "CLASSPATH=${CMAKE_BINARY_DIR}/proton-j/proton-j.jar"
- "SASLPASSWD=${SASLPASSWD_EXE}"
- "SWIG=${SWIG_EXECUTABLE}"
- ${VALGRIND_ENV}
- ${TOX_EXE})
- set_tests_properties(python-tox-test
- PROPERTIES
- PASS_REGULAR_EXPRESSION "Totals: .* ignored, 0 failed"
- FAIL_REGULAR_EXPRESSION "ERROR:[ ]+py[0-9]*: commands failed")
- else (TOX_EXE)
- message(STATUS "The tox tool is not available - skipping the python-tox-tests")
- endif (TOX_EXE)
- endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
+ option(TOX_TEST "Enable muti-version python testing with TOX" ON)
+ if (CMAKE_SYSTEM_NAME STREQUAL Linux AND TOX_TEST)
+ find_program(TOX_EXE "tox")
+ if (TOX_EXE)
+ to_native_path ("${py_path}" py_path)
+ add_test (NAME python-tox-test
+ WORKING_DIRECTORY ${py_src}
+ COMMAND ${env_py}
+ "PATH=${py_path}" "QPID_PROTON_SRC=${CMAKE_CURRENT_SOURCE_DIR}/../"
+ "CLASSPATH=${CMAKE_BINARY_DIR}/proton-j/proton-j.jar"
+ "SASLPASSWD=${SASLPASSWD_EXE}"
+ "SWIG=${SWIG_EXECUTABLE}"
+ ${VALGRIND_ENV}
+ ${TOX_EXE})
+ set_tests_properties(python-tox-test
+ PROPERTIES
+ PASS_REGULAR_EXPRESSION "Totals: .* ignored, 0 failed"
+ FAIL_REGULAR_EXPRESSION "ERROR:[ ]+py[0-9]*: commands failed")
+ else (TOX_EXE)
+ message(STATUS "The tox tool is not available - skipping the python-tox-tests")
+ endif (TOX_EXE)
+ endif (CMAKE_SYSTEM_NAME STREQUAL Linux AND TOX_TEST)
endif (BUILD_PYTHON)
@@ -589,6 +573,3 @@ if (BUILD_JAVASCRIPT)
add_test (javascript-codec ${env_py} node ${pn_test_root}/javascript/codec.js)
add_test (javascript-message ${env_py} node ${pn_test_root}/javascript/message.js)
endif (BUILD_JAVASCRIPT)
-
-# build examples to make sure they still work
-add_subdirectory(../examples ../examples)
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/proton-c/bindings/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/CMakeLists.txt b/proton-c/bindings/CMakeLists.txt
index 41a7882..693e533 100644
--- a/proton-c/bindings/CMakeLists.txt
+++ b/proton-c/bindings/CMakeLists.txt
@@ -17,91 +17,13 @@
# under the License.
#
-include(UseSWIG)
+# Add bindings that do not require swig here - the directory name must be the same as the binding name
+# See below for swig bindings
+set(BINDINGS javascript cpp)
-# Add any new bindings here - the directory name must be the same as the binding name
-set (BINDINGS python ruby php perl javascript)
-
-# All swig modules should include ${PROTON_HEADERS} in SWIG_MODULE_<name>_EXTRA_DEPS
-file(GLOB PROTON_HEADERS "${CMAKE_SOURCE_DIR}/proton-c/include/proton/*.h")
-
-# If swig version is 3.0 or greater then we can build some additional bindings
-if (${SWIG_VERSION} VERSION_GREATER 3.0 OR ${SWIG_VERSION} VERSION_EQUAL 3.0)
- set (BINDINGS
- ${BINDINGS}
- node
- )
-endif()
-
-set (BINDING_DEPS qpid-proton)
-
-# Add a block here to detect the prerequisites to build each language binding:
+# Prerequisites for javascript.
#
-# If the prerequisites for the binding are present set a variable called
-# DEFAULT_{uppercase name of binding} to ON
-
-# Prerequisites for Python wrapper (must be same version as python):
-find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT)
-if (PYTHONLIBS_FOUND)
- set (DEFAULT_PYTHON ON)
-endif (PYTHONLIBS_FOUND)
-
-# Prerequisites for Ruby:
-find_program(GEM_EXE "gem")
-mark_as_advanced(GEM_EXE)
-macro(CheckRubyGem varname gemname)
- execute_process(COMMAND ${GEM_EXE} list --local ${gemname}
- OUTPUT_VARIABLE CHECK_OUTPUT)
-
- set (${varname} OFF)
-
- if (CHECK_OUTPUT MATCHES "${gemname}[ ]+\(.*\)")
- message(STATUS "Found Ruby gem: ${gemname}")
- set (${varname} ON)
- else()
- message(STATUS "Missing Ruby gem dependency: ${gemname}")
- set (${varname} OFF)
- endif()
-endmacro()
-
-find_package(Ruby)
-if (RUBY_FOUND)
- set (DEFAULT_RUBY ON)
-
- CheckRubyGem("HAS_RUBY_GEM_RSPEC" "rspec")
- CheckRubyGem("HAS_RUBY_GEM_SIMPLECOV" "simplecov")
-
- if (HAS_RUBY_GEM_RSPEC AND HAS_RUBY_GEM_SIMPLECOV)
- set (DEFAULT_RUBY_TESTING ON CACHE INTERNAL "")
- else()
- message(STATUS "Skipping Ruby bindings due to missing dependencies...")
- set (DEFAULT_RUBY_TESTING OFF CACHE INTERNAL "")
- set (DEFAULT_RUBY OFF)
- endif (HAS_RUBY_GEM_RSPEC AND HAS_RUBY_GEM_SIMPLECOV)
-endif (RUBY_FOUND)
-
-# Prerequites for PHP:
-# For now, assume PHP support if the 'php-config' tool is present.
-# @todo: allow user to specify which php-config if multiple PHP sources installed!
-find_program(PHP_CONFIG_EXE php-config)
-if (PHP_CONFIG_EXE)
- find_program(PHP_EXE php)
- mark_as_advanced (PHP_EXE)
- if (PHP_EXE)
- set (DEFAULT_PHP ON)
- endif (PHP_EXE)
-endif (PHP_CONFIG_EXE)
-mark_as_advanced (PHP_CONFIG_EXE)
-
-# Prerequisites for Perl:
-include(ProtonFindPerl)
-if (PERLLIBS_FOUND)
- set (DEFAULT_PERL ON)
-endif (PERLLIBS_FOUND)
-
-# Prerequisites for the javascript "binding":
-# This is somewhat different to the other language bindings in that it does not use swig. It uses a C/C++ to
-# JavaScript cross-compiler called emscripten (https://github.com/kripken/emscripten). Emscripten takes C/C++
+# It uses a C/C++ to JavaScript cross-compiler called emscripten (https://github.com/kripken/emscripten). Emscripten takes C/C++
# and compiles it into a highly optimisable subset of JavaScript called asm.js (http://asmjs.org/) that can be
# aggressively optimised and run at near-native speed (usually between 1.5 to 10 times slower than native C/C++).
find_package(Emscripten)
@@ -109,6 +31,93 @@ if (EMSCRIPTEN_FOUND)
set (DEFAULT_JAVASCRIPT ON)
endif (EMSCRIPTEN_FOUND)
+# Prerequisites for C++
+if (CMAKE_CXX_COMPILER)
+ set (DEFAULT_CPP ON)
+endif (CMAKE_CXX_COMPILER)
+
+if(SWIG_FOUND)
+ # Add any new swig bindings here - the directory name must be the same as the binding name
+ list(APPEND BINDINGS python ruby php perl)
+
+ include(UseSWIG)
+
+ # All swig modules should include ${PROTON_HEADERS} in SWIG_MODULE_<name>_EXTRA_DEPS
+ file(GLOB PROTON_HEADERS "${CMAKE_SOURCE_DIR}/proton-c/include/proton/*.h")
+ # All swig modules should include ${BINDING_DEPS} in swig_link_libraries
+ set (BINDING_DEPS qpid-proton)
+
+ # If swig version is 3.0 or greater then we can build some additional bindings
+ if (${SWIG_VERSION} VERSION_GREATER 3.0 OR ${SWIG_VERSION} VERSION_EQUAL 3.0)
+ list(APPEND SWIG_BINDINGS node)
+ endif()
+
+ # Add a block here to detect the prerequisites to build each language binding:
+ #
+ # If the prerequisites for the binding are present set a variable called
+ # DEFAULT_{uppercase name of binding} to ON
+
+ # Prerequisites for Python wrapper:
+ find_package (PythonLibs ${PYTHON_VERSION_STRING} EXACT)
+ if (PYTHONLIBS_FOUND)
+ set (DEFAULT_PYTHON ON)
+ endif (PYTHONLIBS_FOUND)
+
+ # Prerequisites for Ruby:
+ find_program(GEM_EXE "gem")
+ mark_as_advanced(GEM_EXE)
+ macro(CheckRubyGem varname gemname)
+ execute_process(COMMAND ${GEM_EXE} list --local ${gemname}
+ OUTPUT_VARIABLE CHECK_OUTPUT)
+
+ set (${varname} OFF)
+
+ if (CHECK_OUTPUT MATCHES "${gemname}[ ]+\(.*\)")
+ message(STATUS "Found Ruby gem: ${gemname}")
+ set (${varname} ON)
+ else()
+ message(STATUS "Missing Ruby gem dependency: ${gemname}")
+ set (${varname} OFF)
+ endif()
+ endmacro()
+
+ find_package(Ruby)
+ if (RUBY_FOUND)
+ set (DEFAULT_RUBY ON)
+
+ CheckRubyGem("HAS_RUBY_GEM_RSPEC" "rspec")
+ CheckRubyGem("HAS_RUBY_GEM_SIMPLECOV" "simplecov")
+
+ if (HAS_RUBY_GEM_RSPEC AND HAS_RUBY_GEM_SIMPLECOV)
+ set (DEFAULT_RUBY_TESTING ON CACHE INTERNAL "")
+ else()
+ message(STATUS "Skipping Ruby bindings due to missing dependencies...")
+ set (DEFAULT_RUBY_TESTING OFF CACHE INTERNAL "")
+ set (DEFAULT_RUBY OFF)
+ endif (HAS_RUBY_GEM_RSPEC AND HAS_RUBY_GEM_SIMPLECOV)
+ endif (RUBY_FOUND)
+
+ # Prerequites for PHP:
+ # For now, assume PHP support if the 'php-config' tool is present.
+ # @todo: allow user to specify which php-config if multiple PHP sources installed!
+ find_program(PHP_CONFIG_EXE php-config)
+ if (PHP_CONFIG_EXE)
+ find_program(PHP_EXE php)
+ mark_as_advanced (PHP_EXE)
+ if (PHP_EXE)
+ set (DEFAULT_PHP ON)
+ endif (PHP_EXE)
+ endif (PHP_CONFIG_EXE)
+ mark_as_advanced (PHP_CONFIG_EXE)
+
+ # Prerequisites for Perl:
+ include(ProtonFindPerl)
+ if (PERLLIBS_FOUND)
+ set (DEFAULT_PERL ON)
+ endif (PERLLIBS_FOUND)
+
+endif()
+
# Shouldn't need to modify below here when adding new language binding
foreach(BINDING ${BINDINGS})
string(TOUPPER ${BINDING} UBINDING)
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/proton-c/bindings/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt
new file mode 100644
index 0000000..df770fa
--- /dev/null
+++ b/proton-c/bindings/cpp/CMakeLists.txt
@@ -0,0 +1,151 @@
+#
+# 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(CheckCXXSourceCompiles)
+
+# Check compiler capabilities.
+set(CMAKE_REQUIRED_FLAGS "${CXX_WARNING_FLAGS}")
+check_cxx_source_compiles("long long ll; int main(int, char**) { return 0; }" HAS_LONG_LONG)
+if (HAS_LONG_LONG)
+ add_definitions(-DPN_HAS_LONG_LONG=1)
+endif()
+check_cxx_source_compiles("#include <memory>\nstd::shared_ptr<int> i; std::unique_ptr<int> u; int main(int, char**) { return 0; }" HAS_STD_PTR)
+if (HAS_STD_PTR)
+ add_definitions(-DPN_HAS_STD_PTR=1)
+endif()
+
+# Check for boost
+find_path(BOOST_INCLUDE_DIR boost/shared_ptr.hpp PATH_SUFFIXES include)
+if (BOOST_INCLUDE_DIR)
+ add_definitions(-DPN_HAS_BOOST=1)
+endif()
+
+include_directories(
+ "${CMAKE_SOURCE_DIR}/proton-c/include"
+ "${CMAKE_CURRENT_SOURCE_DIR}/include"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src")
+
+set(qpid-proton-cpp-source
+ src/acceptor.cpp
+ src/blocking_connection.cpp
+ src/blocking_connection_impl.cpp
+ src/blocking_fetcher.cpp
+ src/blocking_link.cpp
+ src/blocking_receiver.cpp
+ src/blocking_sender.cpp
+ src/connection.cpp
+ src/connector.cpp
+ src/container.cpp
+ src/container_impl.cpp
+ src/contexts.cpp
+ src/data.cpp
+ src/decoder.cpp
+ src/delivery.cpp
+ src/duration.cpp
+ src/encoder.cpp
+ src/endpoint.cpp
+ src/error.cpp
+ src/event.cpp
+ src/facade.cpp
+ src/handler.cpp
+ src/link.cpp
+ src/message.cpp
+ src/messaging_adapter.cpp
+ src/messaging_event.cpp
+ src/messaging_handler.cpp
+ src/proton_bits.cpp
+ src/proton_event.cpp
+ src/proton_handler.cpp
+ src/reactor.cpp
+ src/receiver.cpp
+ src/sender.cpp
+ src/session.cpp
+ src/sync_request_response.cpp
+ src/terminus.cpp
+ src/transport.cpp
+ src/types.cpp
+ src/url.cpp
+ src/uuid.cpp
+ )
+
+
+set_source_files_properties (
+ ${qpid-proton-cpp-source}
+ PROPERTIES
+ COMPILE_FLAGS "${CXX_WARNING_FLAGS}"
+ )
+
+add_library(qpid-proton-cpp SHARED ${qpid-proton-cpp-source})
+
+target_link_libraries (qpid-proton-cpp ${PLATFORM_LIBS} qpid-proton)
+
+set_target_properties (
+ qpid-proton-cpp
+ PROPERTIES
+ LINKER_LANGUAGE CXX
+ VERSION "${PN_LIB_SOMAJOR}.${PN_LIB_SOMINOR}"
+ SOVERSION "${PN_LIB_SOMAJOR}"
+ LINK_FLAGS "${CATCH_UNDEFINED}"
+ )
+
+## Test
+if (ENABLE_VALGRIND AND VALGRIND_EXE)
+ set(memcheck-cmd ${VALGRIND_EXE} --error-exitcode=1 --quiet --leak-check=full --trace-children=yes)
+endif ()
+
+macro(add_cpp_test test)
+ add_executable (${test} src/${test}.cpp)
+ target_link_libraries (${test} qpid-proton qpid-proton-cpp)
+ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ add_test (NAME cpp_${test}
+ COMMAND ${env_py}
+ "PATH=$<TARGET_FILE_DIR:qpid-proton>"
+ $<TARGET_FILE:${test}> ${ARGN})
+ else ()
+ add_test (NAME cpp_${test} COMMAND ${memcheck-cmd} ${CMAKE_CURRENT_BINARY_DIR}/${test} ${ARGN})
+ endif ()
+endmacro(add_cpp_test)
+
+add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests)
+add_cpp_test(conversion_test ${CMAKE_SOURCE_DIR}/tests)
+
+## Install
+
+install(TARGETS qpid-proton-cpp
+ EXPORT proton
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR})
+
+# Install windows qpid-proton-cpp pdb files
+if (MSVC)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/qpid-proton${CMAKE_DEBUG_POSTFIX}.pdb
+ DESTINATION bin
+ CONFIGURATIONS Debug
+ OPTIONAL)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/qpid-proton.pdb
+ DESTINATION bin
+ CONFIGURATIONS RelWithDebInfo
+ OPTIONAL)
+endif (MSVC)
+
+# Install header files
+file(GLOB headers "include/proton/cpp/*.h")
+install (FILES ${headers} DESTINATION ${INCLUDE_INSTALL_DIR}/proton/cpp)
+
+add_subdirectory(docs)
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/proton-c/bindings/cpp/README.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/README.md b/proton-c/bindings/cpp/README.md
new file mode 100644
index 0000000..d84813b
--- /dev/null
+++ b/proton-c/bindings/cpp/README.md
@@ -0,0 +1,40 @@
+# C++ binding for proton.
+
+This is a C++ binding for the proton API.
+
+The documentation includes a tutorial and API documentation.
+
+To generate the documentation go to your build directory, run `make docs-cpp`
+and open `proton-c/bindings/cpp/docs/html/index.html` in a browser.
+
+# TO DO
+
+Tests
+- Interop/type testing: proton/tests/interop, new interop suite
+- More unit testing, measured code coverage.
+- Test examples against AM-Q and qpidd.
+
+Bugs
+- Error handling:
+ - examples exit silently on broker exit/not running, core on no-such-queue (e.g. with qpidd)
+ - TODO/FIXME notes in code.
+- const correctness: consistent use of const where semantically appropriate in C++ APIs.
+
+Features
+- SASL/SSL support with interop tests.
+- Reconnection
+- Browsing
+- Selectors
+- AMQP described types and arrays, full support and tests.
+- Durable subscriptions & demos (see python changes)
+- Transactions
+- Heartbeats
+
+# Nice to have
+
+- C++11 lambda version of handlers.
+- Helpers (or at least doc) for multi-threaded use (container per connection)
+- Usable support for decimal types.
+- Expose endpoint conditions as C++ proton::condition error class.
+- Selectables and 3rd party event loop support
+- More efficient shared_ptr (single family per proton object.)
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/proton-c/bindings/cpp/docs/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/docs/CMakeLists.txt b/proton-c/bindings/cpp/docs/CMakeLists.txt
new file mode 100644
index 0000000..9c2d0d4
--- /dev/null
+++ b/proton-c/bindings/cpp/docs/CMakeLists.txt
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+find_package(Doxygen)
+if (DOXYGEN_FOUND)
+ configure_file (${CMAKE_CURRENT_SOURCE_DIR}/user.doxygen.in
+ ${CMAKE_CURRENT_BINARY_DIR}/user.doxygen)
+ add_custom_target (docs-cpp COMMAND ${DOXYGEN_EXECUTABLE} user.doxygen)
+ add_dependencies (docs docs-cpp)
+ # HTML files are generated to ./html - put those in the install.
+ install (DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html/"
+ DESTINATION "${PROTON_SHARE}/docs/api-cpp"
+ COMPONENT documentation
+ ${OPTIONAL_ARG})
+endif (DOXYGEN_FOUND)
+
+set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES html)
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/proton-c/bindings/cpp/docs/mainpage.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/docs/mainpage.md b/proton-c/bindings/cpp/docs/mainpage.md
new file mode 100644
index 0000000..b370b13
--- /dev/null
+++ b/proton-c/bindings/cpp/docs/mainpage.md
@@ -0,0 +1,123 @@
+Introduction {#mainpage}
+============
+
+This is the C++ API for the proton AMQP protocol engine. It allows you to write
+client and server applications that send and receive AMQP messages.
+
+The best way to start is with the \ref tutorial "tutorial".
+
+An overview of the model
+------------------------
+
+Messages are transferred between connected peers over 'links'. At the sending
+peer the link is called a sender. At the receiving peer it is called a
+receiver. Messages are sent by senders and received by receivers. Links may have
+named 'source' and 'target' addresses (see "sources and targets" below.)
+
+Links are established over sessions. Sessions are established over
+connections. Connections are (generally) established between two uniquely
+identified containers. Though a connection can have multiple sessions, often
+this is not needed. The container API allows you to ignore sessions unless you
+actually require them.
+
+The sending of a message over a link is called a delivery. The message
+is the content sent, including all meta-data such as headers and
+annotations. The delivery is the protocol exchange associated with the
+transfer of that content.
+
+To indicate that a delivery is complete, either the sender or the
+receiver 'settles' it. When the other side learns that it has been
+settled, they will no longer communicate about that delivery. The
+receiver can also indicate whether they accept or reject the
+message.
+
+Three different delivery levels or 'guarantees' can be achieved: at-most-once,
+at-least-once or exactly-once. See below for details.
+
+### Sources and targets ###
+
+Every link has two addresses, *source* and *target*. The most common pattern for
+using these addresses is as follows:
+
+When a client creates a *receiver* link, it sets the *source* address. This
+means "I want to receive messages from this source." This is often referred to
+as "subscribing" to the source. When a client creates a *sender* link, it sets
+the *target* address. This means "I want to send to this target."
+
+In the case of a broker, the source or target usually refers to a queue or
+topic. In general they can refer to any AMQP-capable node.
+
+In the *request-response* pattern, a request message carries a *reply-to*
+address for the response message. This can be any AMQP address, but it is often
+useful to create a temporary address for just the response message.
+
+The most common approach is for the client to create a *receiver* for the
+response with the *dynamic* flag set. This asks the server to generate a unique
+*source* address automatically and discard it when the link closes. The client
+uses this "dynamic" source address as the reply-to when it sends the request,
+and the response is delivered to the client's dynamic receiver.
+
+In the case of a broker a dynamic address usually corresponds to a temporary
+queue but any AMQP request-response server can use this technique. The \ref
+server_direct.cpp example illustrates how to implement a queueless
+request-response server.
+
+Commonly used classes
+---------------------
+
+A brief summary of some of the key classes follows.
+
+The `proton::container` class is the main entry point into the API, allowing
+connections and links to be established. Applications are structured as one or
+more event handlers.
+
+The easiest way to implement a handler is to define a subclass of
+`proton::messaging_handler` and over-ride the event handling member functions
+that interest you.
+
+To send messages, call `proton::container::create_sender()` to create a
+`proton::sender` and then call `proton::sender::send()`. This is typically done
+when the sender is *sendable*, a condition indicated by the
+`proton::messaging_handler::on_sendable()` event, to avoid execessive build up
+of messages.
+
+To receive messages, call `proton::container::create_receiver()` to create a
+`proton::receiver`. When messages are recieved the
+`proton::messaging_handler::on_message()` event handler function will be called.
+
+Other key classes:
+
+- proton::message represents an AMQP message, which has a body, headers and other properties.
+- proton::connection represents a connection to a remote AMQP endpoint.
+- proton::delivery holds the delivery status of a message.
+- proton::event carries details of an event.
+
+The library provides intuitive default conversions between AMQP and C++ types
+for message data q, but also allows detailed examination and construction of
+complex AMQP types. For details on converting between AMQP and C++ data types
+see the \ref encode_decode.cpp example and classes `proton::encoder`,
+`proton::decoder` and `proton::value`.
+
+Delivery Guarantees
+-------------------
+
+For *at-most-once*, the sender settles the message as soon as it sends
+it. If the connection is lost before the message is received by the
+receiver, the message will not be delivered.
+
+For *at-least-once*, the receiver accepts and settles the message on
+receipt. If the connection is lost before the sender is informed of
+the settlement, then the delivery is considered in-doubt and should be
+retried. This will ensure it eventually gets delivered (provided of
+course the connection and link can be reestablished). It may mean that
+it is delivered multiple times though.
+
+Finally, for *exactly-once*, the receiver accepts the message but
+doesn't settle it. The sender settles once it is aware that the
+receiver accepted it. In this way the receiver retains knowledge of an
+accepted message until it is sure the sender knows it has been
+accepted. If the connection is lost before settlement, the receiver
+informs the sender of all the unsettled deliveries it knows about, and
+from this the sender can deduce which need to be redelivered. The
+sender likewise informs the receiver which deliveries it knows about,
+from which the receiver can deduce which have already been settled.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/445f8873/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
new file mode 100644
index 0000000..56f3e9f
--- /dev/null
+++ b/proton-c/bindings/cpp/docs/tutorial.hpp
@@ -0,0 +1,427 @@
+// -*-markdown-*-
+// NOTE: doxygen can include markdown pages directly but there seems to be a bug
+// that shows messed-up line numbers in \skip \until code extracts so this file
+// is markdown wrapped in a C++ comment - which works.
+
+/*! \page tutorial Tutorial
+
+This is a brief tutorial that will walk you through the fundamentals of building
+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. 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".
+
+The `helloworld` examples take an optional URL argument. The other examples take
+an option `-a URL`. A URL looks like:
+
+ 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`
+
+Hello World!
+------------
+
+\dontinclude helloworld.cpp
+
+Tradition dictates that we start with hello world! This example sends a message
+to a broker and the receives the same message back to demonstrate sending and
+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 mentioned above.
+
+\skip proton/container
+\until proton/url
+
+We will define a class `hello_world` which is a subclass of
+`proton::messaging_handler` and over-rides functions to handle the events
+of interest in sending and receiving a message.
+
+\skip class hello_world
+\until {}
+
+`on_start()` is called when the event loop first starts. We handle that by
+establishing a connection and creating a sender and a receiver.
+
+\skip on_start
+\until }
+
+`on_sendable()` is called when message can be transferred over the associated
+sender link to the remote peer. We create a `proton::message`, set the message
+body to `"Hello World!"` and send the message. Then we close the sender as we only
+want to send one message. Closing the sender will prevent further calls to
+`on_sendable()`.
+
+\skip on_sendable
+\until }
+
+`on_message()` is called when a message is received. We just print the body of
+the message and close the connection, as we only want one message
+
+\skip on_message
+\until }
+
+The message body is a `proton::value`, see the documentation for more on how to
+extract the message body as type-safe C++ values.
+
+Our `main` function creates an instance of the `hello_world` handler and a
+proton::container using that handler. Calling `proton::container::run` sets
+things in motion and returns when we close the connection as there is nothing
+further to do. It may throw an exception, which will be a subclass of
+`proton::error`. That in turn is a subclass of `std::exception`.
+
+\skip main
+\until }
+\until }
+\until }
+
+Hello World, Direct!
+--------------------
+
+\dontinclude helloworld_direct.cpp
+
+Though often used in conjunction with a broker, AMQP does not *require* this. It
+also allows senders and receivers can communicate directly if desired.
+
+We will modify our example to send a message directly to itself. This is a bit
+contrived but illustrates both sides of the direct send/receive scenario. Full
+code at \ref helloworld_direct.cpp
+
+The first difference, is that rather than creating a receiver on the same
+connection as our sender, we listen for incoming connections by invoking the
+`proton::container::Xblisten()` method on the container.
+
+\skip on_start
+\until }
+
+As we only need then to initiate one link, the sender, we can do that by
+passing in a url rather than an existing connection, and the connection
+will also be automatically established for us.
+
+We send the message in response to the `on_sendable()` callback and
+print the message out in response to the `on_message()` callback exactly
+as before.
+
+\skip on_sendable
+\until }
+\until }
+
+However we also handle two new events. We now close the connection from
+the senders side once the message has been accepted.
+The acceptance of the message is an indication of successful transfer to the
+peer. We are notified of that event through the `on_accepted()`
+callback.
+
+\skip on_accepted
+\until }
+
+Then, once the connection has been closed, of which we are
+notified through the `on_closed()` callback, we stop accepting incoming
+connections at which point there is no work to be done and the
+event loop exits, and the run() method will return.
+
+\skip on_closed
+\until }
+
+So now we have our example working without a broker involved!
+
+Note that for this example we paick an "unusual" port 8888 since we are talking
+to ourselves rather than a broker.
+
+\skipline url =
+
+Asynchronous Send and Receive
+-----------------------------
+
+Of course, these `HelloWorld!` examples are very artificial, communicating as
+they do over a network connection but with the same process. A more realistic
+example involves communication between separate processes (which could indeed be
+running on completely separate machines).
+
+Let's separate the sender from the receiver, and transfer more than a single
+message between them.
+
+We'll start with a simple sender \ref simple_send.cpp.
+
+\dontinclude simple_send.cpp
+
+As with the previous example, we define the application logic in a class that
+handles events. Because we are transferring more than one message, we need to
+keep track of how many we have sent. We'll use a `sent` member variable for
+that. The `total` member variable will hold the number of messages we want to
+send.
+
+\skip class simple_send
+\until total
+
+As before, we use the `on_start()` event to establish our sender link over which
+we will transfer messages.
+
+\skip on_start
+\until }
+
+AMQP defines a credit-based flow control mechanism. Flow control allows
+the receiver to control how many messages it is prepared to receive at a
+given time and thus prevents any component being overwhelmed by the
+number of messages it is sent.
+
+In the `on_sendable()` callback, we check that our sender has credit
+before sending messages. We also check that we haven't already sent the
+required number of messages.
+
+\skip on_sendable
+\until }
+\until }
+
+The `proton::sender::send()` call above is asynchronous. When it returns the
+message has not yet actually been transferred across the network to the
+receiver. By handling the `on_accepted()` event, we can get notified when the
+receiver has received and accepted the message. In our example we use this event
+to track the confirmation of the messages we have sent. We only close the
+connection and exit when the receiver has received all the messages we wanted to
+send.
+
+\skip on_accepted
+\until }
+\until }
+
+If we are disconnected after a message is sent and before it has been
+confirmed by the receiver, it is said to be `in doubt`. We don't know
+whether or not it was received. In this example, we will handle that by
+resending any in-doubt messages. This is known as an 'at-least-once'
+guarantee, since each message should eventually be received at least
+once, though a given message may be received more than once (i.e.
+duplicates are possible). In the `on_disconnected()` callback, we reset
+the sent count to reflect only those that have been confirmed. The
+library will automatically try to reconnect for us, and when our sender
+is sendable again, we can restart from the point we know the receiver
+got to.
+
+\skip on_disconnected
+\until }
+
+\dontinclude simple_recv.cpp
+
+Now let's look at the corresponding receiver \ref simple_recv.cpp
+
+This time we'll use an `expected` member variable for for the number of messages we expecct and
+a `received` variable to count how many we have received so far.send.
+
+\skip class simple_recv
+\until received
+
+We handle `on_start()` by creating our receiver, much like we
+did for the sender.
+
+\skip on_start
+\until }
+
+We also handle the `on_message()` event for received messages and print the
+message out as in the `Hello World!` examples. However we add some logic to
+allow the receiver to wait for a given number of messages, then to close the
+connection and exit. We also add some logic to check for and ignore duplicates,
+using a simple sequential id scheme.
+
+\skip on_message
+\until }
+
+Direct Send and Receive
+-----------------------
+
+Sending between these two examples requires an intermediary broker since neither
+accepts incoming connections. AMQP allows us to send messages directly between
+two processes. In that case one or other of the processes needs to accept
+incoming connections. Let's create a modified version of the receiving example
+that does this with \ref direct_recv.cpp
+
+\dontinclude direct_recv.cpp
+
+There are only two differences here. Instead of initiating a link (and
+implicitly a connection), we listen for incoming connections.
+
+
+\skip on_start
+\until }
+
+When we have received all the expected messages, we then stop listening for
+incoming connections by closing the acceptor object.
+
+\skip on_message
+\until }
+\until }
+\until }
+\until }
+
+You can use the \ref simple_send.cpp example to send to this receiver
+directly. (Note: you will need to stop any broker that is listening on the 5672
+port, or else change the port used by specifying a different address to each
+example via the -a command line switch).
+
+We can also modify the sender to allow the original receiver to connect to it,
+in \ref direct_send.cpp. Again that just requires two modifications:
+
+\dontinclude direct_send.cpp
+
+As with the modified receiver, instead of initiating establishment of a
+link, we listen for incoming connections.
+
+\skip on_start
+\until }
+
+When we have received confirmation of all the messages we sent, we can
+close the acceptor in order to exit.
+
+\skip on_accepted
+\until }
+\until }
+
+To try this modified sender, run the original \ref simple_recv.cpp against it.
+
+The symmetry in the underlying AMQP that enables this is quite unique and
+elegant, and in reflecting this the proton API provides a flexible toolkit for
+implementing all sorts of interesting intermediaries (\ref broker.cpp provided
+as a simple broker for testing purposes is an example of this).
+
+Request/Response
+----------------
+
+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.
+
+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 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.
+
+\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.
+
+\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 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
+-----------------------
+
+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:
+
+\dontinclude server_direct.cpp
+
+Our 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 }
+
+*/
+
+/* 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
+of the headers. The following example shows how that can be achieved:
+
+\todo TODO selected_recv.cpp
+
+When creating the receiver, we specify a Selector object as an option.
+The options argument can take a single object or a list. Another option
+that is sometimes of interest when using a broker is the ability to
+'browse' the messages on a queue, rather than consumig them. This is
+done in AMQP by specifying a distribution mode of 'copy' (instead of
+'move' which is the expected default for queues). An example of that is
+shown next:
+
+\todo TODO queue_browser.cpp
+*/
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org