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 2017/11/07 19:26:43 UTC

[01/10] qpid-proton git commit: PROTON-1679: [cpp] Updated test comments to reference JIRA

Repository: qpid-proton
Updated Branches:
  refs/heads/master a9d940f51 -> 51cda9d5d


PROTON-1679: [cpp] Updated test comments to reference JIRA


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

Branch: refs/heads/master
Commit: 82681ada64832bbf93ca3fa60f52a95d112f08f7
Parents: a9d940f
Author: Alan Conway <ac...@redhat.com>
Authored: Tue Nov 7 11:38:09 2017 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:48 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/cpp/src/connection_driver_test.cpp | 4 ++--
 proton-c/bindings/cpp/src/listener.cpp               | 2 --
 2 files changed, 2 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/82681ada/proton-c/bindings/cpp/src/connection_driver_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/connection_driver_test.cpp b/proton-c/bindings/cpp/src/connection_driver_test.cpp
index ed0e830..9197caf 100644
--- a/proton-c/bindings/cpp/src/connection_driver_test.cpp
+++ b/proton-c/bindings/cpp/src/connection_driver_test.cpp
@@ -352,12 +352,12 @@ void test_link_options() {
 
     proton::sender ax = quick_pop(ha.senders);
     ASSERT_EQUAL("_x", ax.name());
-    // FIXME aconway 2017-11-03:
+    // TODO PROTON-1679 - the following assertion should pass.
     // ASSERT_EQUAL("x", ax.target().address());
 
     proton::receiver ay = quick_pop(ha.receivers);
     ASSERT_EQUAL("_y", ay.name());
-    // FIXME aconway 2017-11-03:
+    // TODO PROTON-1679 - the following assertion should pass.
     // ASSERT_EQUAL("y", ay.source().address());
 
     proton::receiver bx = quick_pop(hb.receivers);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/82681ada/proton-c/bindings/cpp/src/listener.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/listener.cpp b/proton-c/bindings/cpp/src/listener.cpp
index 646cfe1..53f238c 100644
--- a/proton-c/bindings/cpp/src/listener.cpp
+++ b/proton-c/bindings/cpp/src/listener.cpp
@@ -33,8 +33,6 @@ listener::listener(pn_listener_t* l): listener_(l) {}
 listener::listener(const listener& l) : listener_(l.listener_) {}
 listener::~listener() {}
 listener& listener::operator=(const listener& l) { listener_ = l.listener_; return *this; }
-
-// FIXME aconway 2017-10-06: should be a no-op if already closed - there is a race here.
 void listener::stop() { if (listener_) pn_listener_close(listener_); }
 
 listen_handler::~listen_handler() {}


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


[04/10] qpid-proton git commit: PROTON-1064: [ruby] remove obsolete examples

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/simple_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/simple_send.rb b/examples/ruby/simple_send.rb
new file mode 100644
index 0000000..38857f3
--- /dev/null
+++ b/examples/ruby/simple_send.rb
@@ -0,0 +1,73 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+class SimpleSend < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url, expected)
+    super()
+    @url = url
+    @sent = 0
+    @confirmed = 0
+    @expected = expected
+  end
+
+  def on_start(event)
+    event.container.create_sender(@url)
+  end
+
+  def on_sendable(event)
+    while event.sender.credit > 0 && @sent < @expected
+      msg = Qpid::Proton::Message.new("sequence #{@sent}", { :id => @sent } )
+      event.sender.send(msg)
+      @sent = @sent + 1
+    end
+  end
+
+  def on_accepted(event)
+    @confirmed = @confirmed + 1
+    if @confirmed == @expected
+      puts "All #{@expected} messages confirmed!"
+      event.connection.close
+    end
+  end
+
+end
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/wrapper_test.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/wrapper_test.rb b/examples/ruby/wrapper_test.rb
deleted file mode 100644
index ca7e250..0000000
--- a/examples/ruby/wrapper_test.rb
+++ /dev/null
@@ -1,82 +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.
-#++
-
-require 'qpid_proton'
-
-def how_many_transports?(expected)
-  count = ObjectSpace.each_object(Qpid::Proton::Transport).count
-  if expected.min == expected.max
-    expectation = "#{expected.min}"
-  else
-    expectation = "#{expected.min} <= count <= #{expected.max}"
-  end
-  puts "Transport count: found #{count}, expected #{expectation} (#{expected.include?(count) ? 'Good' : 'Bad'})"
-end
-
-transport = Qpid::Proton::Transport.new
-timpl = transport.impl
-
-puts "================================="
-puts "= Storing my original transport ="
-puts "================================="
-puts "   Stored transport=#{transport} (#{Cproton.pni_address_of(timpl).to_s(16)})"
-how_many_transports?(1..1)
-puts "================================="
-transport.instance_eval { @first_name = "Darryl"; @last_name = "Pierce", @instance_id = 717 }
-transport = nil
-
-
-puts ""
-max = 1000
-puts "Creating #{max} instances of Transport"
-(0...max).each do |which|
-  t = Qpid::Proton::Transport.new
-  t.instance_eval { @instance_id = which }
-  t = nil
-end
-
-puts ""
-puts "===================================="
-puts "= Retrieving my original transport ="
-puts "===================================="
-transport = Qpid::Proton::Transport.wrap(timpl)
-puts "Retrieved transport=#{transport} (#{Cproton.pni_address_of(timpl).to_s(16)})"
-how_many_transports?(1..1001)
-puts "===================================="
-puts "My transport attributes:"
-puts transport
-
-transport = nil
-GC.start
-how_many_transports?(1..1)
-
-puts ""
-puts "======================================"
-puts "= Throwing away the Transport object ="
-puts "======================================"
-transport = nil
-timpl.instance_eval { @proton_wrapper = nil }
-GC.start
-begin
-  transport = Qpid::Proton::Transport.wrap(timpl)
-  puts "!!! This should fail!"
-rescue Qpid::Proton::ProtonError => error
-  puts "Good, it failed..."
-end
-how_many_transports?(0..0)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index b6ce4a7..3f79af6 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -559,6 +559,21 @@ elseif (PROACTOR AND NOT PROACTOR STREQUAL "none")
   message(FATAL_ERROR "Cannot build the ${PROACTOR} proactor")
 endif()
 
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+  # No change needed for windows already use correct separator
+  function(to_native_path path result)
+    file (TO_NATIVE_PATH "${path}" path)
+    set (${result} ${path} PARENT_SCOPE)
+  endfunction()
+else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+  # Just change ';'->':'
+  function(to_native_path path result)
+    file (TO_NATIVE_PATH "${path}" path)
+    string (REGEX REPLACE ";" ":" path "${path}")
+    set (${result} ${path} PARENT_SCOPE)
+  endfunction()
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
 # note: process bindings after the source lists have been defined so
 # the bindings can reference them
 add_subdirectory(bindings)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/proton-c/bindings/ruby/lib/core/message.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/message.rb b/proton-c/bindings/ruby/lib/core/message.rb
index f31ce29..d84e795 100644
--- a/proton-c/bindings/ruby/lib/core/message.rb
+++ b/proton-c/bindings/ruby/lib/core/message.rb
@@ -129,13 +129,21 @@ module Qpid::Proton
     end
 
     # Creates a new +Message+ instance.
-    def initialize(body = nil)
+    # @param body the body of the message, equivalent to calling m.body=(body)
+    # @param set [Hash] additional settings, equivalent to m.key=value for each key=>value in settings
+    def initialize(body = nil, settings={})
       @impl = Cproton.pn_message
       ObjectSpace.define_finalizer(self, self.class.finalize!(@impl))
       @properties = {}
       @instructions = {}
       @annotations = {}
       self.body = body unless body.nil?
+      if !settings.nil? then
+        settings.each do |k, v|
+          setter = (k.to_s+"=").to_sym()
+          self.send setter, v
+        end
+      end
     end
 
     def to_s

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/proton-c/bindings/ruby/lib/messenger/messenger.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/messenger/messenger.rb b/proton-c/bindings/ruby/lib/messenger/messenger.rb
index 8c4714a..06ce031 100644
--- a/proton-c/bindings/ruby/lib/messenger/messenger.rb
+++ b/proton-c/bindings/ruby/lib/messenger/messenger.rb
@@ -18,6 +18,7 @@
 #
 
 module Qpid::Proton::Messenger
+  # @deprecated use {Qpid::Proton::Container}
   #
   # The +Messenger+ class defines a high level interface for
   # sending and receiving Messages. Every Messenger contains
@@ -72,6 +73,7 @@ module Qpid::Proton::Messenger
     # * name - the name (def. nil)
     #
     def initialize(name = nil)
+      warn "[DEPRECATION] `Qpid::Proton::Messenger` is deprecated, use `Qpid::Proton::Container`"
       @impl = Cproton.pn_messenger(name)
       @selectables = {}
       ObjectSpace.define_finalizer(self, self.class.finalize!(@impl))

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/proton-c/bindings/ruby/spec/messenger_spec.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/spec/messenger_spec.rb b/proton-c/bindings/ruby/spec/messenger_spec.rb
deleted file mode 100644
index 0fec6b9..0000000
--- a/proton-c/bindings/ruby/spec/messenger_spec.rb
+++ /dev/null
@@ -1,393 +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.
-#
-
-require "spec_helper"
-
-module Qpid
-
-  module Proton
-
-    describe "A messenger" do
-
-      before (:each) do
-        @messenger = Qpid::Proton::Messenger::Messenger.new
-      end
-
-      after (:each) do
-        begin
-          @messenger.stop
-        rescue ProtonError => error
-          # ignore this error
-        end
-      end
-
-      it "will generate a name if one is not provided" do
-        expect(@messenger.name).wont_be_nil
-      end
-
-      it "will accept an assigned name" do
-        name = random_string(16)
-        msgr = Qpid::Proton::Messenger::Messenger.new(name)
-        expect(msgr.name).must_equal(name)
-      end
-
-      it "raises an error on a nil timeout" do
-        expect {
-          @messenger.timeout = nil
-        }.must_raise(TypeError)
-      end
-
-      it "can have a negative timeout" do
-        timeout = (0 - rand(65535))
-        @messenger.timeout = timeout
-        expect(@messenger.timeout).must_equal(timeout)
-      end
-
-      it "has a timeout" do
-        timeout = rand(65535)
-        @messenger.timeout = timeout
-        expect(@messenger.timeout).must_equal(timeout)
-      end
-
-      it "has an error number" do
-        expect(@messenger.error?).must_equal(false)
-        expect(@messenger.errno).must_equal(0)
-        # force an error
-        expect {
-          @messenger.subscribe("amqp://~#{random_string}")
-        }.must_raise(ProtonError)
-        expect(@messenger.error?).must_equal(true)
-        expect(@messenger.errno).wont_equal(0)
-      end
-
-      it "has an error message" do
-        expect(@messenger.error?).must_equal(false)
-        expect(@messenger.error).must_be_nil
-        # force an error
-        expect {
-          @messenger.subscribe("amqp://~#{random_string}")
-        }.must_raise(ProtonError)
-        expect(@messenger.error?).must_equal(true)
-        expect(@messenger.errno).wont_be_nil
-      end
-
-      it "can be started" do
-          @messenger.start
-      end
-
-      it "can be stopped" do
-          @messenger.stop
-      end
-
-      it "raises an error when subscribing to a nil address" do
-        expect {
-          @messenger.subscribe(nil)
-        }.must_raise(TypeError)
-      end
-
-      it "raises an error when subscribing to an invalid address" do
-        expect {
-          @messenger.subscribe("amqp://~#{random_string}")
-        }.must_raise(ProtonError)
-        expect(@messenger.error?).must_equal(true)
-        expect(@messenger.errno).wont_equal(nil)
-      end
-
-      it "can have a nil certificate" do
-          @messenger.certificate = nil
-          expect(@messenger.certificate).must_be_nil
-      end
-
-      it "can have a certificate" do
-        cert = random_string(128)
-        @messenger.certificate = cert
-        expect(@messenger.certificate).must_equal(cert)
-      end
-
-      it "can have a nil private key" do
-          @messenger.private_key = nil
-          expect(@messenger.private_key).must_be_nil
-      end
-
-      it "can have a private key" do
-        key = random_string(128)
-        @messenger.private_key = key
-        expect(@messenger.private_key).must_equal(key)
-      end
-
-      it "can have a nil trusted certificates" do
-          @messenger.trusted_certificates = nil
-          expect(@messenger.trusted_certificates).must_be_nil
-      end
-
-      it "has a list of trusted certificates" do
-        certs = random_string(128)
-        @messenger.trusted_certificates = certs
-        expect(@messenger.trusted_certificates).must_equal(certs)
-      end
-
-      it "raises an error on a nil outgoing window" do
-        expect {
-          @messenger.outgoing_window = nil
-        }.must_raise(TypeError)
-      end
-
-      it "raises an error on a non-numeric outgoing window" do
-        expect {
-          @messenger.outgoing_window = random_string(16)
-        }.must_raise(TypeError)
-      end
-
-      it "can have a negative outgoing window" do
-        window = 0 - (rand(256) + 1)
-        @messenger.outgoing_window = window
-        expect(@messenger.outgoing_window).must_equal(window)
-      end
-
-      it "can have a positive outgoing window" do
-        window = (rand(256) + 1)
-        @messenger.outgoing_window = window
-        expect(@messenger.outgoing_window).must_equal(window)
-      end
-
-      it "can have a zero outgoing window" do
-        window = 0
-        @messenger.outgoing_window = window
-        expect(@messenger.outgoing_window).must_equal(window)
-      end
-
-      it "raises an error on a nil incoming window" do
-        expect {
-          @messenger.incoming_window = nil
-        }.must_raise(TypeError)
-      end
-
-      it "raises an error on a non-numeric incoming window" do
-        expect {
-          @messenger.incoming_window = random_string(16)
-        }.must_raise(TypeError)
-      end
-
-      it "can have a negative incoming window" do
-        window = 0 - (rand(256) + 1)
-        @messenger.incoming_window = window
-        expect(@messenger.incoming_window).must_equal(window)
-      end
-
-      it "can have a positive incoming window" do
-        window = (rand(256) + 1)
-        @messenger.incoming_window = window
-        expect(@messenger.incoming_window).must_equal(window)
-      end
-
-      it "can have a zero incoming window" do
-        window = 0
-        @messenger.incoming_window = window
-        expect(@messenger.incoming_window).must_equal(window)
-      end
-
-      it "can be put into passive mode" do
-        @messenger.passive = true
-        expect(@messenger.passive?).must_equal(true)
-      end
-
-      it "can be taken out of passive mode" do
-        @messenger.passive = false
-        expect(@messenger.passive?).must_equal(false)
-      end
-
-      it "can clear non-existent errors with failing" do
-          @messenger.clear_error
-      end
-
-      it "can clear errors" do
-        begin
-          @messenger.accept # should cause an error
-        rescue; end
-
-        expect(@messenger.error).wont_be_nil
-        @messenger.clear_error
-        expect(@messenger.error).must_be_nil
-      end
-
-      describe "once started" do
-
-        before (:each) do
-          @messenger.start
-        end
-
-        after (:each) do
-          begin
-            @messenger.stop
-          rescue ProtonError => error
-            # ignore this error
-          end
-        end
-
-        it "can subscribe to an address" do
-          expect(@messenger.subscribe("amqp://~0.0.0.0:#{5700+rand(1024)}")).wont_be_nil
-        end
-
-        it "returns a tracker's status"
-
-        describe "and subscribed to an address" do
-
-          before (:each) do
-            # create a receiver
-            @port = 5700 + rand(1024)
-            @receiver = Qpid::Proton::Messenger::Messenger.new("receiver")
-            @receiver.subscribe("amqp://~0.0.0.0:#{@port}")
-            @messenger.timeout = 0
-            @receiver.timeout = 0
-            @receiver.start
-
-            Thread.new do
-              @receiver.receive(10)
-            end
-
-            @msg = Qpid::Proton::Message.new
-            @msg.address = "amqp://0.0.0.0:#{@port}"
-            @msg.body = "Test sent #{Time.new}"
-          end
-
-          after (:each) do
-            begin
-              @messenger.stop
-            rescue ProtonError => error
-              # ignore this error
-            end
-            begin
-              @receiver.stop
-            rescue
-            end
-          end
-
-          it "raises an error when queueing a nil message" do
-            expect {
-              @messenger.put(nil)
-            }.must_raise(TypeError)
-          end
-
-          it "raises an error when queueing an invalid object" do
-            expect {
-              @messenger.put("This is not a message")
-            }.must_raise(::ArgumentError)
-          end
-
-          it "can place a message in the outgoing queue" do
-            @messenger.put(@msg)
-          end
-
-          it "can send with an empty queue"
-
-          describe "with a an outgoing tracker" do
-
-            before(:each) do
-              @messenger.put(@msg)
-              @tracker = @messenger.outgoing_tracker
-            end
-
-            it "has an outgoing tracker" do
-              expect(@tracker).wont_be_nil
-            end
-
-            it "returns a tracker's status"
-
-            it "raises an error when settling with a nil tracker" do
-              expect {
-                @messenger.settle(nil)
-              }.must_raise(TypeError)
-            end
-
-            it "can settle a tracker's status" do
-              @messenger.settle(@tracker)
-            end
-
-            it "raises an error when checking status on a nil tracker" do
-              expect {
-                @messenger.status(nil)
-              }.must_raise(TypeError)
-            end
-
-            it "raises an error when checking status on an invalid tracker" do
-              expect {
-                @messenger.status(random_string(16))
-              }.must_raise(TypeError)
-            end
-
-            it "can check the status of a tracker" do
-              expect(@messenger.status(@tracker)).wont_be_nil
-            end
-
-          end
-
-          it "has an incoming tracker"
-          it "can reject an incoming message"
-
-          it "raises an error when accepting with an invalid tracker" do
-            expect {
-              @messenger.accept(random_string(16))
-            }.must_raise(TypeError)
-          end
-
-          it "can accept a message"
-
-          it "raises an error when rejecting with an invalid tracker" do
-            expect {
-              @messenger.accept(random_string(16))
-            }.must_raise(TypeError)
-          end
-
-          describe "with messages sent" do
-
-            before (:each) do
-              @messenger.put(@msg)
-            end
-
-            it "can send messages"
-
-            it "raises an error when receiving with a nil max" do
-              expect {
-                @messenger.receive(nil)
-              }.must_raise(TypeError)
-            end
-
-            it "raises an error when receiving with a non-numeric max" do
-              expect {
-                @messenger.receive("farkle")
-              }.must_raise(TypeError)
-            end
-
-            it "can receive messages"
-            it "and create a new message when one wasn't provided"
-            it "can get a message from the incoming queue"
-            it "can tell how many outgoing messages are pending"
-            it "can tell how many incoming messages are queued"
-
-          end
-
-        end
-
-      end
-
-    end
-
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/proton-c/bindings/ruby/tests/test_tools.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_tools.rb b/proton-c/bindings/ruby/tests/test_tools.rb
index 22a9040..8de86e2 100644
--- a/proton-c/bindings/ruby/tests/test_tools.rb
+++ b/proton-c/bindings/ruby/tests/test_tools.rb
@@ -35,7 +35,7 @@ def wait_port(port, timeout=5)
     TCPSocket.open("", $port).close
   rescue Errno::ECONNREFUSED
     if Time.now > deadline then
-      raise TestError("timed out waiting for port #{port}")
+      raise TestError, "timed out waiting for port #{port}"
     end
     sleep(0.1)
     retry
@@ -113,15 +113,15 @@ end
 class TestServer < TestHandler
   def initialize
     super
-    @port = TCPServer.open(0) do |s| s.addr[1]; end # find an unused port
+    @server = TCPServer.open(0)
   end
 
   def host() ""; end
-  def port() @port; end
+  def port() @server.addr[1]; end
   def addr() "#{host}:#{port}"; end
 
   def on_start(e)
     super
-    @listener = e.container.listen(addr)
+    @listener = e.container.listen_io(@server)
   end
 end


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


[07/10] qpid-proton git commit: PROTON-1064: [ruby] Pass options to open connections/sessions/links

Posted by ac...@apache.org.
PROTON-1064: [ruby] Pass options to open connections/sessions/links

Consistent option passing to endpoints

Minor cleanup:
- Removed UUID class, use standard library
- Fixed ruby code that was out of date with C library.
- Remove dead code.


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

Branch: refs/heads/master
Commit: b3d1b0742225b53ea2c4d950cc62788584d62f3c
Parents: 3d25815
Author: Alan Conway <ac...@redhat.com>
Authored: Mon Sep 18 22:34:42 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/ruby/lib/codec/data.rb        |   4 +-
 proton-c/bindings/ruby/lib/codec/mapping.rb     |  29 ++--
 proton-c/bindings/ruby/lib/core/connection.rb   |  79 ++++++-----
 proton-c/bindings/ruby/lib/core/session.rb      |  54 ++++----
 proton-c/bindings/ruby/lib/core/terminus.rb     |   8 +-
 proton-c/bindings/ruby/lib/core/transport.rb    |  10 ++
 proton-c/bindings/ruby/lib/core/url.rb          |   4 +-
 proton-c/bindings/ruby/lib/event/collector.rb   |   2 +-
 proton-c/bindings/ruby/lib/qpid_proton.rb       |   1 -
 proton-c/bindings/ruby/lib/reactor/backoff.rb   |  10 +-
 proton-c/bindings/ruby/lib/reactor/connector.rb |   8 +-
 proton-c/bindings/ruby/lib/reactor/container.rb | 134 ++++++-------------
 .../bindings/ruby/lib/reactor/link_option.rb    |  13 +-
 proton-c/bindings/ruby/lib/util/uuid.rb         |  32 -----
 proton-c/bindings/ruby/spec/array_spec.rb       |   2 +-
 proton-c/bindings/ruby/tests/test_smoke.rb      |  64 ---------
 16 files changed, 170 insertions(+), 284 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/codec/data.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/codec/data.rb b/proton-c/bindings/ruby/lib/codec/data.rb
index afd4e31..be13512 100644
--- a/proton-c/bindings/ruby/lib/codec/data.rb
+++ b/proton-c/bindings/ruby/lib/codec/data.rb
@@ -849,10 +849,10 @@ module Qpid::Proton::Codec
 
     # Puts a symbolic value.
     #
-    # @param value [String] The symbolic string value.
+    # @param value [String|Symbol] The symbolic string value.
     #
     def symbol=(value)
-      check(Cproton.pn_data_put_symbol(@data, value))
+      check(Cproton.pn_data_put_symbol(@data, value.to_s))
     end
 
     # If the current node is a symbol, returns its value. Otherwise, it

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/codec/mapping.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/codec/mapping.rb b/proton-c/bindings/ruby/lib/codec/mapping.rb
index 421aadc..5a498b4 100644
--- a/proton-c/bindings/ruby/lib/codec/mapping.rb
+++ b/proton-c/bindings/ruby/lib/codec/mapping.rb
@@ -105,11 +105,18 @@ module Qpid::Proton::Codec
   DECIMAL128 = Mapping.new(Cproton::PN_DECIMAL128, "decimal128")
   UUID       = Mapping.new(Cproton::PN_UUID, "uuid")
   BINARY     = Mapping.new(Cproton::PN_BINARY, "binary")
-  STRING     = Mapping.new(Cproton::PN_STRING, "string", [String, Symbol,
+  STRING     = Mapping.new(Cproton::PN_STRING, "string", [::String,
                                                           Qpid::Proton::Types::UTFString,
                                                           Qpid::Proton::Types::BinaryString])
+  SYMBOL     = Mapping.new(Cproton::PN_SYMBOL, "symbol", [::Symbol])
+  DESCRIBED  = Mapping.new(Cproton::PN_DESCRIBED, "described", [Qpid::Proton::Types::Described], "get_described")
+  ARRAY      = Mapping.new(Cproton::PN_ARRAY, "array", nil, "get_array")
+  LIST       = Mapping.new(Cproton::PN_LIST, "list", [::Array], "get_array")
+  MAP        = Mapping.new(Cproton::PN_MAP, "map", [::Hash], "get_map")
+
+
+  private
 
-  # @private
   class << STRING
     def put(data, value)
       # if we have a symbol then convert it to a string
@@ -134,17 +141,9 @@ module Qpid::Proton::Codec
 
       data.string = value if isutf
       data.binary = value if !isutf
-
     end
   end
 
-  SYMBOL     = Mapping.new(Cproton::PN_SYMBOL, "symbol")
-  DESCRIBED  = Mapping.new(Cproton::PN_DESCRIBED, "described", [Qpid::Proton::Types::Described], "get_described")
-  ARRAY      = Mapping.new(Cproton::PN_ARRAY, "array", nil, "get_array")
-  LIST       = Mapping.new(Cproton::PN_LIST, "list", [::Array], "get_array")
-  MAP        = Mapping.new(Cproton::PN_MAP, "map", [::Hash], "get_map")
-
-  # @private
   class << MAP
     def put(data, map, options = {})
       data.put_map
@@ -166,4 +165,14 @@ module Qpid::Proton::Codec
     end
   end
 
+  class << DESCRIBED
+    def put(data, described)
+      data.put_described
+      data.enter
+      data.object = described.descriptor
+      data.object = described.value
+      data.exit
+    end
+  end
+
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/core/connection.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/connection.rb b/proton-c/bindings/ruby/lib/core/connection.rb
index 784b193..ef785b2 100644
--- a/proton-c/bindings/ruby/lib/core/connection.rb
+++ b/proton-c/bindings/ruby/lib/core/connection.rb
@@ -19,15 +19,14 @@
 
 module Qpid::Proton
 
-  # A Connection has at most one Qpid::Proton::Transport instance.
-  #
+  # An AMQP connection.
   class Connection < Endpoint
 
-    # @private
+    protected
+    PROTON_METHOD_PREFIX = "pn_connection"
     include Util::SwigHelper
 
-    # @private
-    PROTON_METHOD_PREFIX = "pn_connection"
+    public
 
     # @!attribute hostname
     #
@@ -45,8 +44,6 @@ module Qpid::Proton
     proton_writer :password
 
     # @private
-    proton_reader :attachments
-
     attr_accessor :overrides
     attr_accessor :session_policy
 
@@ -127,15 +124,10 @@ module Qpid::Proton
       @collector = collector
     end
 
-    # Get the AMQP container name advertised by the remote connection
-    # endpoint.
+    # Get the AMQP container name advertised by the remote connection.
     #
     # This will return nil until the REMOTE_ACTIVE state is reached.
     #
-    # Any non-nil container returned by this operation will be valid
-    # until the connection is unbound from a transport, or freed,
-    # whichever happens sooner.
-    #
     # @return [String] The remote connection's AMQP container name.
     #
     # @see #container
@@ -144,11 +136,8 @@ module Qpid::Proton
       Cproton.pn_connection_remote_container(@impl)
     end
 
-    def container=(name)
-      Cproton.pn_connection_set_container(@impl, name)
-    end
-
-    def container
+    # AMQP container ID string for the local end of the connection.
+    def container_id
       Cproton.pn_connection_get_container(@impl)
     end
 
@@ -204,18 +193,28 @@ module Qpid::Proton
       data_to_object(Cproton.pn_connection_remote_properites(@impl))
     end
 
-    # Opens the connection.
+    # Open the local end of the connection.
+    #
+    # @option options [String] :container_id Unique AMQP container ID, defaults to a UUID
+    # @option [String] :link_prefix Prefix for generated link names, default is container_id
     #
-    def open
-      object_to_data(@offered_capabilities,
-                     Cproton.pn_connection_offered_capabilities(@impl))
-      object_to_data(@desired_capabilities,
-                     Cproton.pn_connection_desired_capabilities(@impl))
-      object_to_data(@properties,
-                     Cproton.pn_connection_properties(@impl))
+    def open(options={})
+      object_to_data(@offered_capabilities, Cproton.pn_connection_offered_capabilities(@impl))
+      object_to_data(@desired_capabilities, Cproton.pn_connection_desired_capabilities(@impl))
+      object_to_data(@properties, Cproton.pn_connection_properties(@impl))
+      cid = options[:container_id] || SecureRandom.uuid
+      Cproton.pn_connection_set_container(@impl, cid)
+      @link_prefix = options[:link_prefix] || cid
+      @link_prefix = SecureRandom.uuid if !@link_prefix || @link_prefix.empty?
+      @link_count = 0
       Cproton.pn_connection_open(@impl)
     end
 
+    # @private Generate a unique link name, internal use only.
+    def link_name()
+      @link_prefix + "/" +  (@link_count += 1).to_s(16)
+    end
+
     # Closes the connection.
     #
     # Once this operation has completed, the #LOCAL_CLOSED state flag will be
@@ -239,14 +238,30 @@ module Qpid::Proton
       Cproton.pn_connection_state(@impl)
     end
 
-    # Returns the session for this connection.
+    # Returns the default session for this connection.
     #
     # @return [Session] The session.
     #
-    def session
-      @session ||= Session.wrap(Cproton.pn_session(@impl))
+    def default_session
+      @session ||= open_session
+    end
+
+    # @deprecated use #default_session()
+    alias_method :session, :default_session
+
+    # Open a new session on this connection.
+    def open_session
+      s = Session.wrap(Cproton.pn_session(@impl))
+      s.open
+      return s
     end
 
+    # Open a sender on the default_session
+    def open_sender(*args, &block) default_session.open_sender(*args, &block) end
+
+    # Open a  on the default_session
+    def open_receiver(*args, &block) default_session.open_receiver(*args, &block) end
+
     # Returns the first session from the connection that matches the specified
     # state mask.
     #
@@ -322,16 +337,16 @@ module Qpid::Proton
       Cproton.pn_error_code(Cproton.pn_connection_error(@impl))
     end
 
-    # @private
+    protected
+
     def _local_condition
       Cproton.pn_connection_condition(@impl)
     end
 
-    # @private
     def _remote_condition
       Cproton.pn_connection_remote_condition(@impl)
     end
 
+    proton_reader :attachments
   end
-
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/core/session.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/session.rb b/proton-c/bindings/ruby/lib/core/session.rb
index 1dbb9bd..81135eb 100644
--- a/proton-c/bindings/ruby/lib/core/session.rb
+++ b/proton-c/bindings/ruby/lib/core/session.rb
@@ -120,40 +120,42 @@ module Qpid::Proton
       Connection.wrap(Cproton.pn_session_connection(@impl))
     end
 
-    # Constructs a new sender.
-    #
-    # Each sender between two AMQP containers must be uniquely named. Note that
-    # this uniqueness cannot be enforced at the library level, so some
-    # consideration should be taken in choosing link names.
-    #
-    # @param name [String] The link name.
-    #
-    # @return [Sender, nil] The sender, or nil if an error occurred.
-    #
-    def sender(name)
-      Sender.new(Cproton.pn_sender(@impl, name))
+    # @deprecated use {#open_sender}
+    def sender(name) Sender.new(Cproton.pn_sender(@impl, name)); end
+
+    # @deprecated use {#open_receiver}
+    def receiver(name) Receiver.new(Cproton.pn_receiver(@impl, name)); end
+
+    # TODO aconway 2016-01-04: doc options or target param
+    def open_receiver(options = {})
+      options = { :source => options } if options.is_a? String
+      receiver = Receiver.new Cproton.pn_receiver(@impl, options[:name] || connection.link_name)
+      receiver.source.address ||= options[:source]
+      receiver.target.address ||= options[:target]
+      receiver.source.dynamic = true if options[:dynamic]
+      receiver.handler = options[:handler] if !options[:handler].nil?
+      receiver.open
+      return receiver
     end
 
-    # Constructs a new receiver.
-    #
-    # Each receiver between two AMQP containers must be uniquely named. Note
-    # that this uniqueness cannot be enforced at the library level, so some
-    # consideration should be taken in choosing link names.
-    #
-    # @param name [String] The link name.
-    #
-    # @return [Receiver, nil] The receiver, or nil if an error occurred.
-    #
-    def receiver(name)
-      Receiver.new(Cproton.pn_receiver(@impl, name))
+    # TODO aconway 2016-01-04: doc options or target param
+    def open_sender(options = {})
+      options = { :target => options } if options.is_a? String
+      sender = Sender.new Cproton.pn_sender(@impl, options[:name] || connection.link_name)
+      sender.target.address ||= options[:target]
+      sender.source.address ||= options[:source]
+      sender.target.dynamic = true if options[:dynamic]
+      sender.handler = options[:handler] if !options[:handler].nil?
+      sender.open
+      return sender
     end
 
-    # @private
+    private
+
     def _local_condition
       Cproton.pn_session_condition(@impl)
     end
 
-    # @private
     def _remote_condition # :nodoc:
       Cproton.pn_session_remote_condition(@impl)
     end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/core/terminus.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/terminus.rb b/proton-c/bindings/ruby/lib/core/terminus.rb
index fa2d8ab..b4927d8 100644
--- a/proton-c/bindings/ruby/lib/core/terminus.rb
+++ b/proton-c/bindings/ruby/lib/core/terminus.rb
@@ -157,7 +157,7 @@ module Qpid::Proton
     # @return [Data] The terminus properties.
     #
     def properties
-      Data.new(Cproton.pn_terminus_properties(@impl))
+      Codec::Data.new(Cproton.pn_terminus_properties(@impl))
     end
 
     # Access and modify the AMQP capabilities data for the Terminus.
@@ -172,7 +172,7 @@ module Qpid::Proton
     # @return [Data] The terminus capabilities.
     #
     def capabilities
-      Data.new(Cproton.pn_terminus_capabilities(@impl))
+      Codec::Data.new(Cproton.pn_terminus_capabilities(@impl))
     end
 
     # Access and modify the AMQP outcomes for the Terminus.
@@ -187,7 +187,7 @@ module Qpid::Proton
     # @return [Data] The terminus outcomes.
     #
     def outcomes
-      Data.new(Cproton.pn_terminus_outcomes(@impl))
+      Codec::Data.new(Cproton.pn_terminus_outcomes(@impl))
     end
 
     # Access and modify the AMQP filter set for the Terminus.
@@ -202,7 +202,7 @@ module Qpid::Proton
     # @return [Data] The terminus filter.
     #
     def filter
-      Data.new(Cproton.pn_terminus_filter(@impl))
+      Codec::Data.new(Cproton.pn_terminus_filter(@impl))
     end
 
     # Copy another Terminus into this instance.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/core/transport.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/transport.rb b/proton-c/bindings/ruby/lib/core/transport.rb
index 04697a3..61767fd 100644
--- a/proton-c/bindings/ruby/lib/core/transport.rb
+++ b/proton-c/bindings/ruby/lib/core/transport.rb
@@ -252,6 +252,16 @@ module Qpid::Proton
       condition_to_object Cproton.pn_transport_condition(@impl)
     end
 
+    # Set the condition of the transport.
+    #
+    # Setting a non-empty condition before closing the transport will cause a
+    # TRANSPORT_ERROR event.
+    #
+    # @param c [Condition] The condition to set
+    def condition=(c)
+      object_to_condition c, Cproton.pn_transport_condition(@impl)
+    end
+
     # Binds to the given connection.
     #
     # @param connection [Connection] The connection.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/core/url.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/url.rb b/proton-c/bindings/ruby/lib/core/url.rb
index 9034487..30d2d87 100644
--- a/proton-c/bindings/ruby/lib/core/url.rb
+++ b/proton-c/bindings/ruby/lib/core/url.rb
@@ -30,9 +30,7 @@ module Qpid::Proton
 
     # Parse a string, return a new URL
     # @param url [#to_s] the URL string
-    def initialize(url = nil, options = {})
-      options[:defaults] = true
-
+    def initialize(url = nil)
       if url
         @url = Cproton.pn_url_parse(url.to_s)
         if @url.nil?

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/event/collector.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/event/collector.rb b/proton-c/bindings/ruby/lib/event/collector.rb
index c86b0f2..74e0182 100644
--- a/proton-c/bindings/ruby/lib/event/collector.rb
+++ b/proton-c/bindings/ruby/lib/event/collector.rb
@@ -112,7 +112,7 @@ module Qpid::Proton::Event
     # @return [nil] if it was elided
     #
     def put(context, event_type)
-      Cproton.pn_collector_put(@impl, Cproton.pn_rb2void(context), event_type.type_code)
+      Cproton.pn_collector_put(@impl, Cproton.pn_class(context.impl), context.impl, event_type.number)
     end
 
     # Access the head event.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/qpid_proton.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/qpid_proton.rb b/proton-c/bindings/ruby/lib/qpid_proton.rb
index 1d614a4..0180291 100644
--- a/proton-c/bindings/ruby/lib/qpid_proton.rb
+++ b/proton-c/bindings/ruby/lib/qpid_proton.rb
@@ -39,7 +39,6 @@ require "util/condition"
 require "util/wrapper"
 require "util/class_wrapper"
 require "util/engine"
-require "util/uuid"
 require "util/timeout"
 require "util/handler"
 require "util/reactor"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/reactor/backoff.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/reactor/backoff.rb b/proton-c/bindings/ruby/lib/reactor/backoff.rb
index 99682e5..54bb401 100644
--- a/proton-c/bindings/ruby/lib/reactor/backoff.rb
+++ b/proton-c/bindings/ruby/lib/reactor/backoff.rb
@@ -21,8 +21,10 @@ module Qpid::Proton::Reactor
 
   class Backoff
 
-    def initialize
-      @delay = 0
+    def initialize min_ = 0, max_ = 3
+      @min = min_ > 0 ? min_ : 0.1
+      @max = [max_, min_].max
+      reset
     end
 
     def reset
@@ -31,11 +33,9 @@ module Qpid::Proton::Reactor
 
     def next
       current = @delay
-      current = 0.1 if current.zero?
-      @delay = [10, 2 * current].min
+      @delay = @delay.zero? ? @min : [@max, 2 * @delay].min
       return current
     end
-
   end
 
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/reactor/connector.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/reactor/connector.rb b/proton-c/bindings/ruby/lib/reactor/connector.rb
index 0971141..b2d0c66 100644
--- a/proton-c/bindings/ruby/lib/reactor/connector.rb
+++ b/proton-c/bindings/ruby/lib/reactor/connector.rb
@@ -21,10 +21,10 @@ module Qpid::Proton::Reactor
 
   class Connector < Qpid::Proton::BaseHandler
 
-    def initialize(connection, url, opts)
-      @connection, @opts = connection, opts
+    def initialize(connection, url, options)
+      @connection, @options = connection, options
       @urls = URLs.new(url) if url
-      opts.each do |k,v|
+      options.each do |k,v|
         case k
         when :url, :urls, :address
           @urls = URLs.new(v) unless @urls
@@ -80,7 +80,7 @@ module Qpid::Proton::Reactor
     def connect(connection)
       url = @urls.next
       transport = Qpid::Proton::Transport.new
-      @opts.each do |k,v|
+      @options.each do |k,v|
         case k
         when :user
           connection.user = v

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/reactor/container.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/reactor/container.rb b/proton-c/bindings/ruby/lib/reactor/container.rb
index 2828bb9..fbe199e 100644
--- a/proton-c/bindings/ruby/lib/reactor/container.rb
+++ b/proton-c/bindings/ruby/lib/reactor/container.rb
@@ -47,19 +47,18 @@ module Qpid::Proton::Reactor
 
     include Qpid::Proton::Util::Reactor
 
-    include Qpid::Proton::Util::UUID
-
     attr_accessor :container_id
     attr_accessor :global_handler
 
-    def initialize(handlers, options = {})
-      super(handlers, options)
+    def initialize(handlers, opts = {})
+      super(handlers, opts)
 
       # only do the following if we're creating a new instance
-      if !options.has_key?(:impl)
+      if !opts.has_key?(:impl)
+        @container_id = String.new(opts[:container_id] || SecureRandom.uuid).freeze
         @ssl = SSLConfig.new
-        if options[:global_handler]
-          self.global_handler = GlobalOverrides.new(options[:global_handler])
+        if opts[:global_handler]
+          self.global_handler = GlobalOverrides.new(opts[:global_handler])
         else
           # very ugly, but using self.global_handler doesn't work in the constructor
           ghandler = Reactor.instance_method(:global_handler).bind(self).call
@@ -67,7 +66,6 @@ module Qpid::Proton::Reactor
           Reactor.instance_method(:global_handler=).bind(self).call(ghandler)
         end
         @trigger = nil
-        @container_id = generate_uuid
       end
     end
 
@@ -95,72 +93,41 @@ module Qpid::Proton::Reactor
     # @return [Connection] the new connection
     #
     def connect(url, opts = {})
-      # Backwards compatible with old connect(options)
+      # Backwards compatible with old connect(opts)
       if url.is_a? Hash and opts.empty?
         opts = url
         url = nil
       end
       conn = self.connection(opts[:handler])
-      conn.container = self.container_id || generate_uuid
       connector = Connector.new(conn, url, opts)
       return conn
     end
 
-    private
-    def _session(context)
-      if context.is_a?(Qpid::Proton::URL)
-        return _session(self.connect(:url => context))
-      elsif context.is_a?(Qpid::Proton::Session)
-        return context
-      elsif context.is_a?(Qpid::Proton::Connection)
-        if context.session_policy?
-          return context.session_policy.session(context)
-        else
-          return self.create_session(context)
-        end
-      else
-        return context.session
-      end
-    end
-
-    public
     # Initiates the establishment of a link over which messages can be sent.
     #
     # @param context [String, URL] The context.
-    # @param opts [Hash] Additional options.
-    # @option opts [String, Qpid::Proton::URL] The target address.
-    # @option opts [String] :source The source address.
-    # @option opts [Boolean] :dynamic
-    # @option opts [Object] :handler
-    # @option opts [Object] :tag_generator The tag generator.
-    # @option opts [Hash] :options Addtional link options
+    # @param opts [Hash] Additional opts.
+    # @param opts [String] :target The target address.
+    # @param opts [String] :source The source address.
+    # @param opts [Boolean] :dynamic
+    # @param opts [Object] :handler
     #
     # @return [Sender] The sender.
     #
-    def create_sender(context, opts = {})
+    def open_sender(context, opts = {})
       if context.is_a?(::String)
         context = Qpid::Proton::URL.new(context)
       end
-
-      target = opts[:target]
-      if context.is_a?(Qpid::Proton::URL) && target.nil?
-        target = context.path
+      if context.is_a?(Qpid::Proton::URL)
+        opts[:target] ||= context.path
       end
 
-      session = _session(context)
-
-      sender = session.sender(opts[:name] ||
-                              id(session.connection.container,
-                                target, opts[:source]))
-        sender.source.address = opts[:source] if !opts[:source].nil?
-        sender.target.address = target if target
-        sender.handler = opts[:handler] if !opts[:handler].nil?
-        sender.tag_generator = opts[:tag_generator] if !opts[:tag_gnenerator].nil?
-        _apply_link_options(opts[:options], sender)
-        sender.open
-        return sender
+      return _session(context).open_sender(opts)
     end
 
+    # @deprecated use @{#open_sender}
+    alias_method :create_sender, :open_sender
+
     # Initiates the establishment of a link over which messages can be received.
     #
     # There are two accepted arguments for the context
@@ -172,41 +139,29 @@ module Qpid::Proton::Reactor
     #
     # The name will be generated for the link if one is not specified.
     #
-    # @param context [Connection, URL, String] The connection or the address.
-    # @param opts [Hash] Additional otpions.
-    # @option opts [String, Qpid::Proton::URL] The source address.
+    # @param context [Connection, URL, String] The connection or the connection address.
+    # @param opts [Hash] Additional opts.
+    # @option opts [String] :source The source address.
     # @option opts [String] :target The target address
     # @option opts [String] :name The link name.
     # @option opts [Boolean] :dynamic
     # @option opts [Object] :handler
-    # @option opts [Hash] :options Additional link options.
     #
-    # @return [Receiver
+    # @return [Receiver]
     #
-    def create_receiver(context, opts = {})
+    def open_receiver(context, opts = {})
       if context.is_a?(::String)
         context = Qpid::Proton::URL.new(context)
       end
-
-      source = opts[:source]
-      if context.is_a?(Qpid::Proton::URL) && source.nil?
-        source = context.path
+      if context.is_a?(Qpid::Proton::URL)
+        opts[:source] ||= context.path
       end
-
-      session = _session(context)
-
-      receiver = session.receiver(opts[:name] ||
-                                  id(session.connection.container,
-                                      source, opts[:target]))
-      receiver.source.address = source if source
-      receiver.source.dynamic = true if opts.has_key?(:dynamic) && opts[:dynamic]
-      receiver.target.address = opts[:target] if !opts[:target].nil?
-      receiver.handler = opts[:handler] if !opts[:handler].nil?
-      _apply_link_options(opts[:options], receiver)
-      receiver.open
-      return receiver
+      return _session(context).open_receiver(opts)
     end
 
+    # @deprecated use @{#open_sender}
+    alias_method :create_receiver, :open_receiver
+
     def declare_transaction(context, handler = nil, settle_before_discharge = false)
       if context.respond_to? :txn_ctl && !context.__send__(:txn_ctl).nil?
         class << context
@@ -239,26 +194,25 @@ module Qpid::Proton::Reactor
 
     private
 
-    def id(container, remote, local)
-      if !local.nil? && !remote.nil?
-        "#{container}-#{remote}-#{local}"
-      elsif !local.nil?
-        "#{container}-#{local}"
-      elsif !remote.nil?
-        "#{container}-#{remote}"
+    def _session(context)
+      if context.is_a?(Qpid::Proton::URL)
+        return _session(self.connect(:url => context))
+      elsif context.is_a?(Qpid::Proton::Session)
+        return context
+      elsif context.is_a?(Qpid::Proton::Connection)
+        return context.default_session
       else
-        "#{container}-#{generate_uuid}"
+        return context.session
       end
     end
 
-    def _apply_link_options(options, link)
-      if !options.nil? && !options.empty?
-        if !options.is_a?(::List)
-          options = [Options].flatten
-        end
+    def do_work(timeout = nil)
+      self.timeout = timeout unless timeout.nil?
+      self.process
+    end
 
-        options.each {|option| o.apply(link) if o.test(link)}
-      end
+    def _apply_link_opts(opts, link)
+      opts.each {|o| o.apply(link) if o.test(link)}
     end
 
     def to_s

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/reactor/link_option.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/reactor/link_option.rb b/proton-c/bindings/ruby/lib/reactor/link_option.rb
index 628a811..2066bab 100644
--- a/proton-c/bindings/ruby/lib/reactor/link_option.rb
+++ b/proton-c/bindings/ruby/lib/reactor/link_option.rb
@@ -64,9 +64,9 @@ module Qpid::Proton::Reactor
 
     def apply(link)
       if link.receiver?
-        link.source.properties.dict = @properties
+        link.source.properties.object = @properties
       else
-        link.target.properties.dict = @properties
+        link.target.properties.object = @properties
       end
     end
   end
@@ -77,14 +77,9 @@ module Qpid::Proton::Reactor
     end
 
     def apply(receiver)
-      receiver.source.filter.dict = @filter_set
+      receiver.source.filter.object = @filter_set
     end
-  end
 
-  #class Selector < Filter
-  #  def initialize(value, name = 'selector')
-  #
-  #  end
-  #end
+  end
 
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/lib/util/uuid.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/util/uuid.rb b/proton-c/bindings/ruby/lib/util/uuid.rb
deleted file mode 100644
index 882715b..0000000
--- a/proton-c/bindings/ruby/lib/util/uuid.rb
+++ /dev/null
@@ -1,32 +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.
-#++
-
-module Qpid::Proton::Util
-
-  module UUID
-
-    def generate_uuid
-      # generate a UUID based on what APIs are available with the current
-      # version of Ruby
-      SecureRandom.uuid
-    end
-
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/spec/array_spec.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/spec/array_spec.rb b/proton-c/bindings/ruby/spec/array_spec.rb
index 5d91f1a..3bfe559 100644
--- a/proton-c/bindings/ruby/spec/array_spec.rb
+++ b/proton-c/bindings/ruby/spec/array_spec.rb
@@ -39,7 +39,7 @@ describe "The extended array type" do
   end
 
   it "raises an error when putting into a nil Data object" do
-    expect { @list.proton_put(nil) }.must_raise
+    expect { @list.proton_put(nil) }.must_raise(TypeError)
   end
 
   it "raises an error when getting from a nil Data object" do

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b3d1b074/proton-c/bindings/ruby/tests/test_smoke.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_smoke.rb b/proton-c/bindings/ruby/tests/test_smoke.rb
deleted file mode 100755
index 61cc7cf..0000000
--- a/proton-c/bindings/ruby/tests/test_smoke.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'minitest/autorun'
-require 'minitest/unit'
-require 'qpid_proton'
-
-class SmokeTest < MiniTest::Test
-
-  Messenger = Qpid::Proton::Messenger::Messenger
-  Message = Qpid::Proton::Message
-
-  def setup
-    @server = Messenger.new()
-    @client = Messenger.new()
-    @server.blocking = false
-    @client.blocking = false
-    @server.subscribe("~0.0.0.0:12345")
-    @server.start()
-    @client.start()
-    pump()
-  end
-
-  def pump
-    while (@server.work(0) or @client.work(0)) do end
-  end
-
-  def teardown
-    @server.stop()
-    @client.stop()
-
-    pump()
-
-    assert @client.stopped?
-    assert @server.stopped?
-  end
-
-  def testSmoke(count=10)
-    msg = Message.new()
-    msg.address = "0.0.0.0:12345"
-
-    @server.receive()
-
-    count.times {|i|
-      msg.body = "Hello World! #{i}"
-      @client.put(msg)
-    }
-
-    msg2 = Message.new()
-
-    count.times {|i|
-      if (@server.incoming == 0) then
-        pump()
-      end
-      @server.get(msg2)
-      assert msg2.body == "Hello World! #{i}"
-    }
-
-    assert(@client.outgoing == 0,
-           "Expected 0 outgoing messages, found #{@client.outgoing}")
-    assert(@server.incoming == 0,
-           "Expected 0 incoming messages, found #{@server.incoming}")
-  end
-
-end


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


[06/10] qpid-proton git commit: PROTON-1064: [ruby] Wrap pn_connection_driver_t as ConnectionDriver

Posted by ac...@apache.org.
PROTON-1064: [ruby] Wrap pn_connection_driver_t as ConnectionDriver


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

Branch: refs/heads/master
Commit: 9bb1baad263e094427e863e3455b8bc7dbd1f949
Parents: b3d1b07
Author: Alan Conway <ac...@redhat.com>
Authored: Mon Sep 18 22:31:48 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/ruby/CMakeLists.txt           |   6 +-
 proton-c/bindings/ruby/cproton.i                | 677 +++++++++++++++++++
 proton-c/bindings/ruby/lib/core/connection.rb   |   2 +-
 .../bindings/ruby/lib/core/connection_driver.rb | 182 +++++
 proton-c/bindings/ruby/lib/core/endpoint.rb     |  14 +-
 proton-c/bindings/ruby/lib/qpid_proton.rb       |   1 +
 proton-c/bindings/ruby/ruby.i                   | 640 ------------------
 .../ruby/tests/test_connection_driver.rb        |  70 ++
 proton-c/include/proton/cproton.i               |  13 +-
 9 files changed, 956 insertions(+), 649 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/CMakeLists.txt b/proton-c/bindings/ruby/CMakeLists.txt
index 93364e2..3397b43 100644
--- a/proton-c/bindings/ruby/CMakeLists.txt
+++ b/proton-c/bindings/ruby/CMakeLists.txt
@@ -27,7 +27,7 @@ list(APPEND SWIG_MODULE_cproton-ruby_EXTRA_DEPS
     ${PROTON_HEADERS}
 )
 include_directories (${RUBY_INCLUDE_PATH})
-swig_add_library(cproton-ruby LANGUAGE ruby SOURCES ruby.i)
+swig_add_library(cproton-ruby LANGUAGE ruby SOURCES cproton.i)
 swig_link_libraries(cproton-ruby ${BINDING_DEPS} ${RUBY_LIBRARY})
 
 # set a compiler macro to relay the Ruby version to the extension.
@@ -55,11 +55,11 @@ if (GEM_EXE)
   add_custom_command(
     OUTPUT ${bin}/qpid_proton-${PN_VERSION}.gem
     COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${bin}/gem
-    COMMAND ${CMAKE_COMMAND} -E copy ${bin}/rubyRUBY_wrap.c ${bin}/gem/ext/cproton/cproton.c
+    COMMAND ${CMAKE_COMMAND} -E copy ${bin}/cprotonRUBY_wrap.c ${bin}/gem/ext/cproton/cproton.c
     COMMAND ${GEM_EXE} build qpid_proton.gemspec
     COMMAND ${CMAKE_COMMAND} -E copy ${bin}/gem/qpid_proton-${PN_VERSION}.gem ${bin}
     WORKING_DIRECTORY ${bin}/gem
-    DEPENDS ${RUBY_SRC} ${src}/LICENSE ${src}/TODO ${src}/ChangeLog cproton-ruby ${bin}/rubyRUBY_wrap.c
+    DEPENDS ${RUBY_SRC} ${src}/LICENSE ${src}/TODO ${src}/ChangeLog cproton-ruby ${bin}/cprotonRUBY_wrap.c
     )
 
   add_custom_target(ruby-gem ALL DEPENDS ${bin}/qpid_proton-${PN_VERSION}.gem )

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/cproton.i
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/cproton.i b/proton-c/bindings/ruby/cproton.i
new file mode 100644
index 0000000..107f5d8
--- /dev/null
+++ b/proton-c/bindings/ruby/cproton.i
@@ -0,0 +1,677 @@
+/*
+ * 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.
+ */
+%module cproton
+
+%{
+#include <proton/connection_driver.h>
+#include <proton/engine.h>
+#include <proton/handlers.h>
+#include <proton/message.h>
+#include <proton/messenger.h>
+#include <proton/reactor.h>
+#include <proton/sasl.h>
+#include <proton/ssl.h>
+#include <proton/types.h>
+#include <proton/url.h>
+%}
+
+/*
+NOTE: According to ccache-swig man page: "Known problems are using
+preprocessor directives within %inline blocks and the use of ’#pragma SWIG’."
+This includes using macros in an %inline section.
+
+Keep preprocessor directives and macro expansions in the normal header section.
+*/
+
+%include <cstring.i>
+
+%cstring_output_withsize(char *OUTPUT, size_t *OUTPUT_SIZE)
+%cstring_output_allocate_size(char **ALLOC_OUTPUT, size_t *ALLOC_SIZE, free(*$1));
+%cstring_output_maxsize(char *OUTPUT, size_t MAX_OUTPUT_SIZE)
+
+%{
+#if !defined(RSTRING_LEN)
+#  define RSTRING_LEN(x) (RSTRING(x)->len)
+#  define RSTRING_PTR(x) (RSTRING(x)->ptr)
+#endif
+%}
+
+%typemap(in) pn_bytes_t {
+  if ($input == Qnil) {
+    $1.start = NULL;
+    $1.size = 0;
+  } else {
+    $1.start = RSTRING_PTR($input);
+    if (!$1.start) {
+      $1.size = 0;
+    }
+    $1.size = RSTRING_LEN($input);
+  }
+}
+
+%typemap(out) pn_bytes_t {
+  $result = rb_str_new($1.start, $1.size);
+}
+
+%typemap(in) pn_atom_t
+{
+  if ($input == Qnil)
+    {
+      $1.type = PN_NULL;
+    }
+  else
+    {
+      switch(TYPE($input))
+        {
+        case T_TRUE:
+          $1.type = PN_BOOL;
+          $1.u.as_bool = true;
+          break;
+
+        case T_FALSE:
+          $1.type = PN_BOOL;
+          $1.u.as_bool = false;
+          break;
+
+        case T_FLOAT:
+          $1.type = PN_FLOAT;
+          $1.u.as_float = NUM2DBL($input);
+          break;
+
+        case T_STRING:
+          $1.type = PN_STRING;
+          $1.u.as_bytes.start = RSTRING_PTR($input);
+          if ($1.u.as_bytes.start)
+            {
+              $1.u.as_bytes.size = RSTRING_LEN($input);
+            }
+          else
+            {
+              $1.u.as_bytes.size = 0;
+            }
+          break;
+
+        case T_FIXNUM:
+          $1.type = PN_INT;
+          $1.u.as_int = FIX2LONG($input);
+          break;
+
+        case T_BIGNUM:
+          $1.type = PN_LONG;
+          $1.u.as_long = NUM2LL($input);
+          break;
+
+        }
+    }
+}
+
+%typemap(out) pn_atom_t
+{
+  switch($1.type)
+    {
+    case PN_NULL:
+      $result = Qnil;
+      break;
+
+    case PN_BOOL:
+      $result = $1.u.as_bool ? Qtrue : Qfalse;
+      break;
+
+    case PN_BYTE:
+      $result = INT2NUM($1.u.as_byte);
+      break;
+
+    case PN_UBYTE:
+      $result = UINT2NUM($1.u.as_ubyte);
+      break;
+
+    case PN_SHORT:
+      $result = INT2NUM($1.u.as_short);
+      break;
+
+    case PN_USHORT:
+      $result = UINT2NUM($1.u.as_ushort);
+      break;
+
+    case PN_INT:
+      $result = INT2NUM($1.u.as_int);
+      break;
+
+     case PN_UINT:
+      $result = UINT2NUM($1.u.as_uint);
+      break;
+
+    case PN_LONG:
+      $result = LL2NUM($1.u.as_long);
+      break;
+
+    case PN_ULONG:
+      $result = ULL2NUM($1.u.as_ulong);
+      break;
+
+    case PN_FLOAT:
+      $result = rb_float_new($1.u.as_float);
+      break;
+
+    case PN_DOUBLE:
+      $result = rb_float_new($1.u.as_double);
+      break;
+
+    case PN_STRING:
+      $result = rb_str_new($1.u.as_bytes.start, $1.u.as_bytes.size);
+      break;
+
+    default:
+       break;
+    }
+}
+
+%typemap (in) pn_decimal32_t
+{
+  $1 = FIX2UINT($input);
+}
+
+%typemap (out) pn_decimal32_t
+{
+  $result = ULL2NUM($1);
+}
+
+%typemap (in) pn_decimal64_t
+{
+  $1 = NUM2ULL($input);
+}
+
+%typemap (out) pn_decimal64_t
+{
+  $result = ULL2NUM($1);
+}
+
+%typemap (in) pn_decimal128_t
+{
+  int index;
+
+  for(index = 0; index < 16; index++)
+    {
+      VALUE element = rb_ary_entry($input, index);
+      $1.bytes[16 - (index + 1)] = FIX2INT(element);
+    }
+}
+
+%typemap (out) pn_decimal128_t
+{
+  int index;
+
+  $result = rb_ary_new2(16);
+  for(index = 0; index < 16; index++)
+    {
+      rb_ary_store($result, 16 - (index + 1), CHR2FIX($1.bytes[index]));
+    }
+}
+
+%typemap (in) pn_uuid_t
+{
+  int index;
+
+  for(index = 0; index < 16; index++)
+    {
+      VALUE element = rb_ary_entry($input, index);
+      $1.bytes[16 - (index + 1)] = FIX2INT(element);
+    }
+}
+
+%typemap (out) pn_uuid_t
+{
+  int index;
+
+  $result = rb_ary_new2(16);
+  for(index = 0; index < 16; index++)
+    {
+      rb_ary_store($result, 16 - (index + 1), CHR2FIX($1.bytes[index]));
+    }
+}
+
+int pn_message_encode(pn_message_t *msg, char *OUTPUT, size_t *OUTPUT_SIZE);
+%ignore pn_message_encode;
+
+ssize_t pn_link_send(pn_link_t *transport, char *STRING, size_t LENGTH);
+%ignore pn_link_send;
+
+%rename(pn_link_recv) wrap_pn_link_recv;
+%inline %{
+  int wrap_pn_link_recv(pn_link_t *link, char *OUTPUT, size_t *OUTPUT_SIZE) {
+    ssize_t sz = pn_link_recv(link, OUTPUT, *OUTPUT_SIZE);
+    if (sz >= 0) {
+      *OUTPUT_SIZE = sz;
+    } else {
+      *OUTPUT_SIZE = 0;
+    }
+    return sz;
+  }
+%}
+%ignore pn_link_recv;
+
+ssize_t pn_transport_input(pn_transport_t *transport, char *STRING, size_t LENGTH);
+%ignore pn_transport_input;
+
+%rename(pn_transport_output) wrap_pn_transport_output;
+%inline %{
+  int wrap_pn_transport_output(pn_transport_t *transport, char *OUTPUT, size_t *OUTPUT_SIZE) {
+    ssize_t sz = pn_transport_output(transport, OUTPUT, *OUTPUT_SIZE);
+    if (sz >= 0) {
+      *OUTPUT_SIZE = sz;
+    } else {
+      *OUTPUT_SIZE = 0;
+    }
+    return sz;
+  }
+%}
+%ignore pn_transport_output;
+
+%rename(pn_transport_peek) wrap_pn_transport_peek;
+%inline %{
+  int wrap_pn_transport_peek(pn_transport_t *transport, char *OUTPUT, size_t *OUTPUT_SIZE) {
+    ssize_t sz = pn_transport_peek(transport, OUTPUT, *OUTPUT_SIZE);
+    if(sz >= 0) {
+      *OUTPUT_SIZE = sz;
+    } else {
+      *OUTPUT_SIZE = 0;
+    }
+    return sz;
+  }
+%}
+%ignore pn_transport_peek;
+
+%rename(pn_delivery) wrap_pn_delivery;
+%inline %{
+  pn_delivery_t *wrap_pn_delivery(pn_link_t *link, char *STRING, size_t LENGTH) {
+    return pn_delivery(link, pn_dtag(STRING, LENGTH));
+  }
+%}
+%ignore pn_delivery;
+
+// Suppress "Warning(451): Setting a const char * variable may leak memory." on pn_delivery_tag_t
+%warnfilter(451) pn_delivery_tag_t;
+%rename(pn_delivery_tag) wrap_pn_delivery_tag;
+%inline %{
+  void wrap_pn_delivery_tag(pn_delivery_t *delivery, char **ALLOC_OUTPUT, size_t *ALLOC_SIZE) {
+    pn_delivery_tag_t tag = pn_delivery_tag(delivery);
+    *ALLOC_OUTPUT = malloc(tag.size);
+    *ALLOC_SIZE = tag.size;
+    memcpy(*ALLOC_OUTPUT, tag.start, tag.size);
+  }
+%}
+%ignore pn_delivery_tag;
+
+bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
+%ignore pn_ssl_get_cipher_name;
+
+bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
+%ignore pn_ssl_get_protocol_name;
+
+%inline %{
+#if defined(RUBY20) || defined(RUBY21)
+
+  typedef void *non_blocking_return_t;
+#define RB_BLOCKING_CALL rb_thread_call_without_gvl
+
+#elif defined(RUBY19)
+
+    typedef VALUE non_blocking_return_t;
+#define RB_BLOCKING_CALL rb_thread_blocking_region
+
+#endif
+  %}
+
+%rename(pn_messenger_send) wrap_pn_messenger_send;
+%rename(pn_messenger_recv) wrap_pn_messenger_recv;
+%rename(pn_messenger_work) wrap_pn_messenger_work;
+
+%inline %{
+
+#if defined(RB_BLOCKING_CALL)
+
+    static non_blocking_return_t pn_messenger_send_no_gvl(void *args) {
+    VALUE result = Qnil;
+    pn_messenger_t *messenger = (pn_messenger_t *)((void **)args)[0];
+    int *limit = (int *)((void **)args)[1];
+
+    int rc = pn_messenger_send(messenger, *limit);
+
+    result = INT2NUM(rc);
+    return (non_blocking_return_t )result;
+    }
+
+    static non_blocking_return_t pn_messenger_recv_no_gvl(void *args) {
+    VALUE result = Qnil;
+    pn_messenger_t *messenger = (pn_messenger_t *)((void **)args)[0];
+    int *limit = (int *)((void **)args)[1];
+
+    int rc = pn_messenger_recv(messenger, *limit);
+
+    result = INT2NUM(rc);
+    return (non_blocking_return_t )result;
+  }
+
+    static non_blocking_return_t pn_messenger_work_no_gvl(void *args) {
+      VALUE result = Qnil;
+      pn_messenger_t *messenger = (pn_messenger_t *)((void **)args)[0];
+      int *timeout = (int *)((void **)args)[1];
+
+      int rc = pn_messenger_work(messenger, *timeout);
+
+      result = INT2NUM(rc);
+      return (non_blocking_return_t )result;
+    }
+
+#endif
+
+  int wrap_pn_messenger_send(pn_messenger_t *messenger, int limit) {
+    int result = 0;
+
+#if defined(RB_BLOCKING_CALL)
+
+    // only release the gil if we're blocking
+    if(pn_messenger_is_blocking(messenger)) {
+      VALUE rc;
+      void* args[2];
+
+      args[0] = messenger;
+      args[1] = &limit;
+
+      rc = RB_BLOCKING_CALL(pn_messenger_send_no_gvl,
+                            &args, RUBY_UBF_PROCESS, NULL);
+
+      if(RTEST(rc))
+        {
+          result = FIX2INT(rc);
+        }
+    }
+
+#else // !defined(RB_BLOCKING_CALL)
+    result = pn_messenger_send(messenger, limit);
+#endif // defined(RB_BLOCKING_CALL)
+
+    return result;
+  }
+
+  int wrap_pn_messenger_recv(pn_messenger_t *messenger, int limit) {
+    int result = 0;
+
+#if defined(RB_BLOCKING_CALL)
+    // only release the gil if we're blocking
+    if(pn_messenger_is_blocking(messenger)) {
+      VALUE rc;
+      void* args[2];
+
+      args[0] = messenger;
+      args[1] = &limit;
+
+      rc = RB_BLOCKING_CALL(pn_messenger_recv_no_gvl,
+                            &args, RUBY_UBF_PROCESS, NULL);
+
+      if(RTEST(rc))
+        {
+          result = FIX2INT(rc);
+        }
+
+    } else {
+      result = pn_messenger_recv(messenger, limit);
+    }
+#else // !defined(RB_BLOCKING_CALL)
+    result = pn_messenger_recv(messenger, limit);
+#endif // defined(RB_BLOCKING_CALL)
+
+      return result;
+  }
+
+  int wrap_pn_messenger_work(pn_messenger_t *messenger, int timeout) {
+    int result = 0;
+
+#if defined(RB_BLOCKING_CALL)
+    // only release the gil if we're blocking
+    if(timeout) {
+      VALUE rc;
+      void* args[2];
+
+      args[0] = messenger;
+      args[1] = &timeout;
+
+      rc = RB_BLOCKING_CALL(pn_messenger_work_no_gvl,
+                            &args, RUBY_UBF_PROCESS, NULL);
+
+      if(RTEST(rc))
+        {
+          result = FIX2INT(rc);
+        }
+    } else {
+      result = pn_messenger_work(messenger, timeout);
+    }
+#else
+    result = pn_messenger_work(messenger, timeout);
+#endif
+
+    return result;
+  }
+
+%}
+
+%ignore pn_messenger_send;
+%ignore pn_messenger_recv;
+%ignore pn_messenger_work;
+
+%{
+typedef struct Pn_rbkey_t {
+  void *registry;
+  char *method;
+  char *key_value;
+} Pn_rbkey_t;
+
+void Pn_rbkey_initialize(void *vp_rbkey) {
+  Pn_rbkey_t *rbkey = (Pn_rbkey_t*)vp_rbkey;
+  assert(rbkey);
+  rbkey->registry = NULL;
+  rbkey->method = NULL;
+  rbkey->key_value = NULL;
+}
+
+void Pn_rbkey_finalize(void *vp_rbkey) {
+  Pn_rbkey_t *rbkey = (Pn_rbkey_t*)vp_rbkey;
+  if(rbkey && rbkey->registry && rbkey->method && rbkey->key_value) {
+    rb_funcall((VALUE )rbkey->registry, rb_intern(rbkey->method), 1, rb_str_new2(rbkey->key_value));
+  }
+  if(rbkey->key_value) {
+    free(rbkey->key_value);
+    rbkey->key_value = NULL;
+  }
+}
+
+/* NOTE: no macro or preprocessor definitions in %inline sections */
+#define CID_Pn_rbkey CID_pn_void
+#define Pn_rbkey_inspect NULL
+#define Pn_rbkey_compare NULL
+#define Pn_rbkey_hashcode NULL
+
+pn_class_t* Pn_rbkey__class(void) {
+    static pn_class_t clazz = PN_CLASS(Pn_rbkey);
+    return &clazz;
+}
+
+Pn_rbkey_t *Pn_rbkey_new(void) {
+    return (Pn_rbkey_t *) pn_class_new(Pn_rbkey__class(), sizeof(Pn_rbkey_t));
+}
+%}
+
+pn_class_t* Pn_rbkey__class(void);
+Pn_rbkey_t *Pn_rbkey_new(void);
+
+%inline %{
+
+Pn_rbkey_t *Pn_rbkey_new(void);
+
+void Pn_rbkey_set_registry(Pn_rbkey_t *rbkey, void *registry) {
+  assert(rbkey);
+  rbkey->registry = registry;
+}
+
+void *Pn_rbkey_get_registry(Pn_rbkey_t *rbkey) {
+  assert(rbkey);
+  return rbkey->registry;
+}
+
+void Pn_rbkey_set_method(Pn_rbkey_t *rbkey, char *method) {
+  assert(rbkey);
+  rbkey->method = method;
+}
+
+char *Pn_rbkey_get_method(Pn_rbkey_t *rbkey) {
+  assert(rbkey);
+  return rbkey->method;
+}
+
+void Pn_rbkey_set_key_value(Pn_rbkey_t *rbkey, char *key_value) {
+  assert(rbkey);
+  rbkey->key_value = malloc(strlen(key_value) + 1);
+  strncpy(rbkey->key_value, key_value, strlen(key_value) + 1);
+}
+
+char *Pn_rbkey_get_key_value(Pn_rbkey_t *rbkey) {
+  assert(rbkey);
+  return rbkey->key_value;
+}
+
+Pn_rbkey_t *pni_void2rbkey(void *object) {
+  return (Pn_rbkey_t *)object;
+}
+
+VALUE pn_void2rb(void *object) {
+  return (VALUE )object;
+}
+
+void *pn_rb2void(VALUE object) {
+  return (void *)object;
+}
+
+VALUE pni_address_of(void *object) {
+  return ULL2NUM((unsigned long )object);
+}
+
+%}
+
+//%rename(pn_collector_put) wrap_pn_collector_put;
+//%inline %{
+//  pn_event_t *wrap_pn_collector_put(pn_collector_t *collector, void *context,
+//                               pn_event_type_t type) {
+//    return pn_collector_put(collector, PN_RBREF, context, type);
+//  }
+//  %}
+//%ignore pn_collector_put;
+
+int pn_ssl_get_peer_hostname(pn_ssl_t *ssl, char *OUTPUT, size_t *OUTPUT_SIZE);
+%ignore pn_ssl_get_peer_hostname;
+
+%inline %{
+
+  VALUE pni_ruby_get_proton_module() {
+    VALUE mQpid = rb_define_module("Qpid");
+    return rb_define_module_under(mQpid, "Proton");
+  }
+
+  void pni_ruby_add_to_registry(VALUE key, VALUE value) {
+    VALUE result = rb_funcall(pni_ruby_get_proton_module(), rb_intern("add_to_registry"), 2, key, value);
+  }
+
+  VALUE pni_ruby_get_from_registry(VALUE key) {
+     return rb_funcall(pni_ruby_get_proton_module(), rb_intern("get_from_registry"), 1, key);
+  }
+
+  void pni_ruby_delete_from_registry(VALUE stored_key) {
+    rb_funcall(pni_ruby_get_proton_module(), rb_intern("delete_from_registry"), 1, stored_key);
+  }
+
+  typedef struct {
+    VALUE handler_key;
+  } Pni_rbhandler_t;
+
+  static Pni_rbhandler_t *pni_rbhandler(pn_handler_t *handler) {
+    return (Pni_rbhandler_t *) pn_handler_mem(handler);
+  }
+
+  static void pni_rbdispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
+    Pni_rbhandler_t *rbh = pni_rbhandler(handler);
+    VALUE rbhandler = pni_ruby_get_from_registry(rbh->handler_key);
+
+    rb_funcall(rbhandler, rb_intern("dispatch"), 2, SWIG_NewPointerObj(event, SWIGTYPE_p_pn_event_t, 0), INT2FIX(type));
+  }
+
+  static void pni_rbhandler_finalize(pn_handler_t *handler) {
+    Pni_rbhandler_t *rbh = pni_rbhandler(handler);
+    pni_ruby_delete_from_registry(rbh->handler_key);
+  }
+
+  pn_handler_t *pn_rbhandler(VALUE handler) {
+    pn_handler_t *chandler = pn_handler_new(pni_rbdispatch, sizeof(Pni_rbhandler_t), pni_rbhandler_finalize);
+    Pni_rbhandler_t *rhy = pni_rbhandler(chandler);
+
+    VALUE ruby_key = rb_class_new_instance(0, NULL, rb_cObject);
+    pni_ruby_add_to_registry(ruby_key, handler);
+
+    rhy->handler_key = ruby_key;
+
+    return chandler;
+  }
+
+
+  /* Helpers for working with pn_connection_driver_t */
+
+  size_t pni_connection_driver_read_size(pn_connection_driver_t* d) {
+    return pn_connection_driver_read_buffer(d).size;
+  }
+
+  size_t pni_connection_driver_write_size(pn_connection_driver_t* d) {
+    return pn_connection_driver_write_buffer(d).size;
+  }
+
+  pn_connection_t *pni_connection_driver_connection(pn_connection_driver_t* d) {
+    return d->connection;
+  }
+
+  pn_transport_t *pni_connection_driver_transport(pn_connection_driver_t* d) {
+    return d->transport;
+  }
+
+  size_t pni_connection_driver_read_copy(pn_connection_driver_t* d, char *STRING, size_t LENGTH ) {
+    pn_rwbytes_t rbuf = pn_connection_driver_read_buffer(d);
+    size_t n = LENGTH < rbuf.size ? LENGTH : rbuf.size;
+    memcpy(rbuf.start, STRING, n);
+    pn_connection_driver_read_done(d, n);
+    return n;
+  }
+
+  pn_connection_driver_t *pni_connection_driver() {
+    pn_connection_driver_t *d = (pn_connection_driver_t*)malloc(sizeof(*d));
+    if (pn_connection_driver_init(d, NULL, NULL) != 0) {
+      free(d);
+      return NULL;
+    }
+    return d;
+  }
+
+%}
+
+%include "proton/cproton.i"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/lib/core/connection.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/connection.rb b/proton-c/bindings/ruby/lib/core/connection.rb
index ef785b2..6caf589 100644
--- a/proton-c/bindings/ruby/lib/core/connection.rb
+++ b/proton-c/bindings/ruby/lib/core/connection.rb
@@ -196,7 +196,7 @@ module Qpid::Proton
     # Open the local end of the connection.
     #
     # @option options [String] :container_id Unique AMQP container ID, defaults to a UUID
-    # @option [String] :link_prefix Prefix for generated link names, default is container_id
+    # @option options [String] :link_prefix Prefix for generated link names, default is container_id
     #
     def open(options={})
       object_to_data(@offered_capabilities, Cproton.pn_connection_offered_capabilities(@impl))

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/lib/core/connection_driver.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/connection_driver.rb b/proton-c/bindings/ruby/lib/core/connection_driver.rb
new file mode 100644
index 0000000..b5b38ac
--- /dev/null
+++ b/proton-c/bindings/ruby/lib/core/connection_driver.rb
@@ -0,0 +1,182 @@
+# 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.
+
+require 'socket'
+
+module Qpid
+  module Proton
+
+    # Associate an AMQP {Connection} with an {IO} and a {MessagingHandler}
+    #
+    # - Read AMQP binary data from the {IO} (#read, #process)
+    # - Call on_* methods on the {MessagingHandler} for AMQP events (#dispatch, #process)
+    # - Write AMQP binary data to the {IO} (#write, #process)
+    #
+    # Thread safety: The {ConnectionDriver} is not thread safe but separate
+    # {ConnectionDriver} instances can be processed concurrently. The
+    # {Container} handles multiple connections concurrently in multiple threads.
+    #
+    class ConnectionDriver
+
+      # Create a {Connection} and associate it with +io+ and +handler+
+      #
+      # @param io [#read_nonblock, #write_nonblock] An {IO} or {IO}-like object that responds
+      #   to #read_nonblock and #write_nonblock.
+      # @param handler [MessagingHandler] The handler to be invoked for AMQP events
+      #
+      def initialize io, handler=nil
+        @impl = Cproton.pni_connection_driver or raise RuntimeError, "cannot create connection driver"
+        @io = io
+        @handler = handler || Handler::MessagingHandler.new # Default handler for default behaviour
+        @rbuf = ""                                          # String to re-use as read buffer
+      end
+
+      # @return [MessagingHandler]
+      attr_reader :handler
+
+      # @return [Connection]
+      def connection() Connection.wrap(Cproton.pni_connection_driver_connection(@impl)); end
+
+      # @return [Transport]
+      def transport() Transport.wrap(Cproton.pni_connection_driver_transport(@impl)); end
+
+      # @return [IO] Allows ConnectionDriver to be passed directly to {IO#select}
+      def to_io() @io; end
+
+      # @return [Bool] True if the driver can read more data
+      def can_read?() Cproton.pni_connection_driver_read_size(@impl) > 0; end
+
+      # @return [Bool] True if the driver has data to write
+      def can_write?() Cproton.pni_connection_driver_write_size(@impl) > 0; end
+
+      # True if read and write sides of the IO are closed. Note this does not imply
+      # {#finished?} since there may still be events to dispatch.
+      def closed?
+        Cproton.pn_connection_driver_read_closed(@impl) &&
+          Cproton.pn_connection_driver_read_closed(@impl)
+      end
+
+      # True if the ConnectionDriver has nothing left to do: {#closed?} and
+      # there are no more events to dispatch.
+      def finished?() Cproton.pn_connection_driver_finished(@impl); end
+
+      # Dispatch available events, call the relevant on_* methods on the {#handler}.
+      def dispatch(extra_handlers = nil)
+        extra_handlers ||= []
+        while event = Event::Event.wrap(Cproton.pn_connection_driver_next_event(@impl))
+          event.dispatch(@handler)
+          extra_handlers.each { |h| event.dispatch h }
+        end
+      end
+
+      # Read from IO without blocking.
+      # IO errors are not raised, they are passed to {#handler}.on_transport_error by {#dispatch}
+      def read
+        size = Cproton.pni_connection_driver_read_size(@impl)
+        return if size <= 0
+        @io.read_nonblock(size, @rbuf) # Use the same string rbuf for reading each time
+        Cproton.pni_connection_driver_read_copy(@impl, @rbuf) unless @rbuf.empty?
+        rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
+          # Try again later.
+        rescue EOFError         # EOF is not an error
+          Cproton.pn_connection_driver_read_close(@impl)
+        rescue IOError => e     # IOError is passed to the transport
+          error "read: #{e}"
+          Cproton.pn_connection_driver_read_close(@impl)
+      end
+
+      # Write to IO without blocking.
+      # IO errors are not raised, they are passed to {#handler}.on_transport_error by {#dispatch}
+      def write
+        n = @io.write_nonblock(Cproton.pn_connection_driver_write_buffer(@impl))
+        Cproton.pn_connection_driver_write_done(@impl, n) if n > 0
+      rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
+        # Try again later.
+      rescue IOError => e
+        error "write: #{e}"
+        Cproton.pn_connection_driver_write_close(@impl)
+      end
+
+      # Generate timed events and IO, for example idle-timeout and heart-beat events.
+      # May generate events for {#dispatch} and change the readable/writeable state.
+      #
+      # @param [Time] now the current time, defaults to {Time#now}.
+      #
+      # @return [Time] time of the next scheduled event, or nil if there are no
+      # scheduled events. If non-nil, tick() must be called again no later than
+      # this time.
+      def tick(now=Time.now)
+        transport = Cproton.pni_connection_driver_transport(@impl)
+        ms = Cproton.pn_transport_tick(transport, (now.to_r * 1000).to_i)
+        return ms.zero? ? nil : Time.at(ms.to_r / 1000);
+      end
+
+      # Do read, tick, write and dispatch without blocking.
+      # @param [Bool] io_readable true if the IO might be readable
+      # @param [Bool] io_writable true if the IO might be writeable
+      # @param [Time] now the current time
+      # @return [Time] Latest time to call {#process} again for scheduled events,
+      # or nil if there are no scheduled events
+      def process(io_readable=true, io_writable=true, now=Time.now)
+        read if io_readable
+        next_tick = tick(now)
+        if io_writable
+          dispatch
+          write
+        end
+        dispatch
+        return next_tick
+      end
+
+      # Close the read side of the IO with optional error.
+      # @param e [#to_s] Non-nil error will call {#handler}.on_transport_error on next {#dispatch}
+      def close_read(e=nil)
+          @io.close_read
+          error(e)
+          Cproton.pn_connection_driver_read_close(@impl)
+      end
+
+      # Close the write side of the IO with optional error
+      # @param e [#to_s] Non-nil error will call {#handler}.on_transport_error on next {#dispatch}
+      def close_write(e=nil)
+          @io.close_write
+          error(e)
+          Cproton.pn_connection_driver_write_close(@impl)
+      end
+
+      # Close both sides of the IO with optional error
+      # @param e [#to_s] Non-nil error will call {#handler}.on_transport_error on next {#dispatch}
+      def close(e=nil)
+        if !closed?
+          close_read(e)
+          close_write(e)
+        end
+      end
+
+      def to_s
+        transport = Cproton.pni_connection_driver_tranport(@impl)
+        return "#<#{self.class.name}[#{transport}]:#{@io}>"
+      end
+
+      private
+
+      def error(e)
+        Cproton.pn_connection_driver_errorf(@impl, "proton:io", "%s", e.to_s) if e
+      end
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/lib/core/endpoint.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/endpoint.rb b/proton-c/bindings/ruby/lib/core/endpoint.rb
index f3ddbcb..7c6f0a3 100644
--- a/proton-c/bindings/ruby/lib/core/endpoint.rb
+++ b/proton-c/bindings/ruby/lib/core/endpoint.rb
@@ -82,6 +82,12 @@ module Qpid::Proton
       self.connection.transport
     end
 
+    # @return [Bool] true if endpoint has sent and received a CLOSE frame
+    def closed?() check_state(LOCAL_CLOSED | REMOTE_CLOSED); end
+
+    # @return [Bool] true if endpoint has sent and received an OPEN frame
+    def open?() check_state(LOCAL_ACTIVE | REMOTE_ACTIVE); end
+
     def local_uninit?
       check_state(LOCAL_UNINIT)
     end
@@ -106,10 +112,6 @@ module Qpid::Proton
       check_state(REMOTE_CLOSED)
     end
 
-    def check_state(state_mask)
-      !(self.state & state_mask).zero?
-    end
-
     def handler
       reactor = Qpid::Proton::Reactor::Reactor.wrap(Cproton.pn_object_reactor(@impl))
       if reactor.nil?
@@ -135,6 +137,10 @@ module Qpid::Proton
       Cproton.pn_decref(impl)
     end
 
+    private
+
+    def check_state(mask) (self.state & mask) == mask; end
+
   end
 
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/lib/qpid_proton.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/qpid_proton.rb b/proton-c/bindings/ruby/lib/qpid_proton.rb
index 0180291..dae9b5f 100644
--- a/proton-c/bindings/ruby/lib/qpid_proton.rb
+++ b/proton-c/bindings/ruby/lib/qpid_proton.rb
@@ -78,6 +78,7 @@ require "core/ssl"
 require "core/transport"
 require "core/base_handler"
 require "core/url"
+require "core/connection_driver"
 
 # Messenger API classes
 require "messenger/subscription"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/ruby.i
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/ruby.i b/proton-c/bindings/ruby/ruby.i
deleted file mode 100644
index d5979f3..0000000
--- a/proton-c/bindings/ruby/ruby.i
+++ /dev/null
@@ -1,640 +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.
- */
-%module cproton
-
-%{
-#include <proton/engine.h>
-#include <proton/message.h>
-#include <proton/sasl.h>
-#include <proton/messenger.h>
-#include <proton/ssl.h>
-#include <proton/types.h>
-#include <proton/url.h>
-#include <proton/reactor.h>
-#include <proton/handlers.h>
-%}
-
-/*
-NOTE: According to ccache-swig man page: "Known problems are using
-preprocessor directives within %inline blocks and the use of ’#pragma SWIG’."
-This includes using macros in an %inline section.
-
-Keep preprocessor directives and macro expansions in the normal header section.
-*/
-
-%include <cstring.i>
-
-%cstring_output_withsize(char *OUTPUT, size_t *OUTPUT_SIZE)
-%cstring_output_allocate_size(char **ALLOC_OUTPUT, size_t *ALLOC_SIZE, free(*$1));
-%cstring_output_maxsize(char *OUTPUT, size_t MAX_OUTPUT_SIZE)
-
-%{
-#if !defined(RSTRING_LEN)
-#  define RSTRING_LEN(x) (RSTRING(X)->len)
-#  define RSTRING_PTR(x) (RSTRING(x)->ptr)
-#endif
-%}
-
-%typemap(in) pn_bytes_t {
-  if ($input == Qnil) {
-    $1.start = NULL;
-    $1.size = 0;
-  } else {
-    $1.start = RSTRING_PTR($input);
-    if (!$1.start) {
-      $1.size = 0;
-    }
-    $1.size = RSTRING_LEN($input);
-  }
-}
-
-%typemap(out) pn_bytes_t {
-  $result = rb_str_new($1.start, $1.size);
-}
-
-%typemap(in) pn_atom_t
-{
-  if ($input == Qnil)
-    {
-      $1.type = PN_NULL;
-    }
-  else
-    {
-      switch(TYPE($input))
-        {
-        case T_TRUE:
-          $1.type = PN_BOOL;
-          $1.u.as_bool = true;
-          break;
-
-        case T_FALSE:
-          $1.type = PN_BOOL;
-          $1.u.as_bool = false;
-          break;
-
-        case T_FLOAT:
-          $1.type = PN_FLOAT;
-          $1.u.as_float = NUM2DBL($input);
-          break;
-
-        case T_STRING:
-          $1.type = PN_STRING;
-          $1.u.as_bytes.start = RSTRING_PTR($input);
-          if ($1.u.as_bytes.start)
-            {
-              $1.u.as_bytes.size = RSTRING_LEN($input);
-            }
-          else
-            {
-              $1.u.as_bytes.size = 0;
-            }
-          break;
-
-        case T_FIXNUM:
-          $1.type = PN_INT;
-          $1.u.as_int = FIX2LONG($input);
-          break;
-
-        case T_BIGNUM:
-          $1.type = PN_LONG;
-          $1.u.as_long = NUM2LL($input);
-          break;
-
-        }
-    }
-}
-
-%typemap(out) pn_atom_t
-{
-  switch($1.type)
-    {
-    case PN_NULL:
-      $result = Qnil;
-      break;
-
-    case PN_BOOL:
-      $result = $1.u.as_bool ? Qtrue : Qfalse;
-      break;
-
-    case PN_BYTE:
-      $result = INT2NUM($1.u.as_byte);
-      break;
-
-    case PN_UBYTE:
-      $result = UINT2NUM($1.u.as_ubyte);
-      break;
-
-    case PN_SHORT:
-      $result = INT2NUM($1.u.as_short);
-      break;
-
-    case PN_USHORT:
-      $result = UINT2NUM($1.u.as_ushort);
-      break;
-
-    case PN_INT:
-      $result = INT2NUM($1.u.as_int);
-      break;
-
-     case PN_UINT:
-      $result = UINT2NUM($1.u.as_uint);
-      break;
-
-    case PN_LONG:
-      $result = LL2NUM($1.u.as_long);
-      break;
-
-    case PN_ULONG:
-      $result = ULL2NUM($1.u.as_ulong);
-      break;
-
-    case PN_FLOAT:
-      $result = rb_float_new($1.u.as_float);
-      break;
-
-    case PN_DOUBLE:
-      $result = rb_float_new($1.u.as_double);
-      break;
-
-    case PN_STRING:
-      $result = rb_str_new($1.u.as_bytes.start, $1.u.as_bytes.size);
-      break;
-
-    default:
-       break;
-    }
-}
-
-%typemap (in) pn_decimal32_t
-{
-  $1 = FIX2UINT($input);
-}
-
-%typemap (out) pn_decimal32_t
-{
-  $result = ULL2NUM($1);
-}
-
-%typemap (in) pn_decimal64_t
-{
-  $1 = NUM2ULL($input);
-}
-
-%typemap (out) pn_decimal64_t
-{
-  $result = ULL2NUM($1);
-}
-
-%typemap (in) pn_decimal128_t
-{
-  int index;
-
-  for(index = 0; index < 16; index++)
-    {
-      VALUE element = rb_ary_entry($input, index);
-      $1.bytes[16 - (index + 1)] = FIX2INT(element);
-    }
-}
-
-%typemap (out) pn_decimal128_t
-{
-  int index;
-
-  $result = rb_ary_new2(16);
-  for(index = 0; index < 16; index++)
-    {
-      rb_ary_store($result, 16 - (index + 1), CHR2FIX($1.bytes[index]));
-    }
-}
-
-%typemap (in) pn_uuid_t
-{
-  int index;
-
-  for(index = 0; index < 16; index++)
-    {
-      VALUE element = rb_ary_entry($input, index);
-      $1.bytes[16 - (index + 1)] = FIX2INT(element);
-    }
-}
-
-%typemap (out) pn_uuid_t
-{
-  int index;
-
-  $result = rb_ary_new2(16);
-  for(index = 0; index < 16; index++)
-    {
-      rb_ary_store($result, 16 - (index + 1), CHR2FIX($1.bytes[index]));
-    }
-}
-
-int pn_message_encode(pn_message_t *msg, char *OUTPUT, size_t *OUTPUT_SIZE);
-%ignore pn_message_encode;
-
-ssize_t pn_link_send(pn_link_t *transport, char *STRING, size_t LENGTH);
-%ignore pn_link_send;
-
-%rename(pn_link_recv) wrap_pn_link_recv;
-%inline %{
-  int wrap_pn_link_recv(pn_link_t *link, char *OUTPUT, size_t *OUTPUT_SIZE) {
-    ssize_t sz = pn_link_recv(link, OUTPUT, *OUTPUT_SIZE);
-    if (sz >= 0) {
-      *OUTPUT_SIZE = sz;
-    } else {
-      *OUTPUT_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_link_recv;
-
-ssize_t pn_transport_input(pn_transport_t *transport, char *STRING, size_t LENGTH);
-%ignore pn_transport_input;
-
-%rename(pn_transport_output) wrap_pn_transport_output;
-%inline %{
-  int wrap_pn_transport_output(pn_transport_t *transport, char *OUTPUT, size_t *OUTPUT_SIZE) {
-    ssize_t sz = pn_transport_output(transport, OUTPUT, *OUTPUT_SIZE);
-    if (sz >= 0) {
-      *OUTPUT_SIZE = sz;
-    } else {
-      *OUTPUT_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_transport_output;
-
-%rename(pn_transport_peek) wrap_pn_transport_peek;
-%inline %{
-  int wrap_pn_transport_peek(pn_transport_t *transport, char *OUTPUT, size_t *OUTPUT_SIZE) {
-    ssize_t sz = pn_transport_peek(transport, OUTPUT, *OUTPUT_SIZE);
-    if(sz >= 0) {
-      *OUTPUT_SIZE = sz;
-    } else {
-      *OUTPUT_SIZE = 0;
-    }
-    return sz;
-  }
-%}
-%ignore pn_transport_peek;
-
-%rename(pn_delivery) wrap_pn_delivery;
-%inline %{
-  pn_delivery_t *wrap_pn_delivery(pn_link_t *link, char *STRING, size_t LENGTH) {
-    return pn_delivery(link, pn_dtag(STRING, LENGTH));
-  }
-%}
-%ignore pn_delivery;
-
-// Suppress "Warning(451): Setting a const char * variable may leak memory." on pn_delivery_tag_t
-%warnfilter(451) pn_delivery_tag_t;
-%rename(pn_delivery_tag) wrap_pn_delivery_tag;
-%inline %{
-  void wrap_pn_delivery_tag(pn_delivery_t *delivery, char **ALLOC_OUTPUT, size_t *ALLOC_SIZE) {
-    pn_delivery_tag_t tag = pn_delivery_tag(delivery);
-    *ALLOC_OUTPUT = malloc(tag.size);
-    *ALLOC_SIZE = tag.size;
-    memcpy(*ALLOC_OUTPUT, tag.start, tag.size);
-  }
-%}
-%ignore pn_delivery_tag;
-
-bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
-%ignore pn_ssl_get_cipher_name;
-
-bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
-%ignore pn_ssl_get_protocol_name;
-
-%inline %{
-#if defined(RUBY20) || defined(RUBY21)
-
-  typedef void *non_blocking_return_t;
-#define RB_BLOCKING_CALL rb_thread_call_without_gvl
-
-#elif defined(RUBY19)
-
-    typedef VALUE non_blocking_return_t;
-#define RB_BLOCKING_CALL rb_thread_blocking_region
-
-#endif
-  %}
-
-%rename(pn_messenger_send) wrap_pn_messenger_send;
-%rename(pn_messenger_recv) wrap_pn_messenger_recv;
-%rename(pn_messenger_work) wrap_pn_messenger_work;
-
-%inline %{
-
-#if defined(RB_BLOCKING_CALL)
-
-    static non_blocking_return_t pn_messenger_send_no_gvl(void *args) {
-    VALUE result = Qnil;
-    pn_messenger_t *messenger = (pn_messenger_t *)((void **)args)[0];
-    int *limit = (int *)((void **)args)[1];
-
-    int rc = pn_messenger_send(messenger, *limit);
-
-    result = INT2NUM(rc);
-    return (non_blocking_return_t )result;
-    }
-
-    static non_blocking_return_t pn_messenger_recv_no_gvl(void *args) {
-    VALUE result = Qnil;
-    pn_messenger_t *messenger = (pn_messenger_t *)((void **)args)[0];
-    int *limit = (int *)((void **)args)[1];
-
-    int rc = pn_messenger_recv(messenger, *limit);
-
-    result = INT2NUM(rc);
-    return (non_blocking_return_t )result;
-  }
-
-    static non_blocking_return_t pn_messenger_work_no_gvl(void *args) {
-      VALUE result = Qnil;
-      pn_messenger_t *messenger = (pn_messenger_t *)((void **)args)[0];
-      int *timeout = (int *)((void **)args)[1];
-
-      int rc = pn_messenger_work(messenger, *timeout);
-
-      result = INT2NUM(rc);
-      return (non_blocking_return_t )result;
-    }
-
-#endif
-
-  int wrap_pn_messenger_send(pn_messenger_t *messenger, int limit) {
-    int result = 0;
-
-#if defined(RB_BLOCKING_CALL)
-
-    // only release the gil if we're blocking
-    if(pn_messenger_is_blocking(messenger)) {
-      VALUE rc;
-      void* args[2];
-
-      args[0] = messenger;
-      args[1] = &limit;
-
-      rc = RB_BLOCKING_CALL(pn_messenger_send_no_gvl,
-                            &args, RUBY_UBF_PROCESS, NULL);
-
-      if(RTEST(rc))
-        {
-          result = FIX2INT(rc);
-        }
-    }
-
-#else // !defined(RB_BLOCKING_CALL)
-    result = pn_messenger_send(messenger, limit);
-#endif // defined(RB_BLOCKING_CALL)
-
-    return result;
-  }
-
-  int wrap_pn_messenger_recv(pn_messenger_t *messenger, int limit) {
-    int result = 0;
-
-#if defined(RB_BLOCKING_CALL)
-    // only release the gil if we're blocking
-    if(pn_messenger_is_blocking(messenger)) {
-      VALUE rc;
-      void* args[2];
-
-      args[0] = messenger;
-      args[1] = &limit;
-
-      rc = RB_BLOCKING_CALL(pn_messenger_recv_no_gvl,
-                            &args, RUBY_UBF_PROCESS, NULL);
-
-      if(RTEST(rc))
-        {
-          result = FIX2INT(rc);
-        }
-
-    } else {
-      result = pn_messenger_recv(messenger, limit);
-    }
-#else // !defined(RB_BLOCKING_CALL)
-    result = pn_messenger_recv(messenger, limit);
-#endif // defined(RB_BLOCKING_CALL)
-
-      return result;
-  }
-
-  int wrap_pn_messenger_work(pn_messenger_t *messenger, int timeout) {
-    int result = 0;
-
-#if defined(RB_BLOCKING_CALL)
-    // only release the gil if we're blocking
-    if(timeout) {
-      VALUE rc;
-      void* args[2];
-
-      args[0] = messenger;
-      args[1] = &timeout;
-
-      rc = RB_BLOCKING_CALL(pn_messenger_work_no_gvl,
-                            &args, RUBY_UBF_PROCESS, NULL);
-
-      if(RTEST(rc))
-        {
-          result = FIX2INT(rc);
-        }
-    } else {
-      result = pn_messenger_work(messenger, timeout);
-    }
-#else
-    result = pn_messenger_work(messenger, timeout);
-#endif
-
-    return result;
-  }
-
-%}
-
-%ignore pn_messenger_send;
-%ignore pn_messenger_recv;
-%ignore pn_messenger_work;
-
-%{
-typedef struct Pn_rbkey_t {
-  void *registry;
-  char *method;
-  char *key_value;
-} Pn_rbkey_t;
-
-void Pn_rbkey_initialize(void *vp_rbkey) {
-  Pn_rbkey_t *rbkey = (Pn_rbkey_t*)vp_rbkey;
-  assert(rbkey);
-  rbkey->registry = NULL;
-  rbkey->method = NULL;
-  rbkey->key_value = NULL;
-}
-
-void Pn_rbkey_finalize(void *vp_rbkey) {
-  Pn_rbkey_t *rbkey = (Pn_rbkey_t*)vp_rbkey;
-  if(rbkey && rbkey->registry && rbkey->method && rbkey->key_value) {
-    rb_funcall((VALUE )rbkey->registry, rb_intern(rbkey->method), 1, rb_str_new2(rbkey->key_value));
-  }
-  if(rbkey->key_value) {
-    free(rbkey->key_value);
-    rbkey->key_value = NULL;
-  }
-}
-
-/* NOTE: no macro or preprocessor definitions in %inline sections */
-#define CID_Pn_rbkey CID_pn_void
-#define Pn_rbkey_inspect NULL
-#define Pn_rbkey_compare NULL
-#define Pn_rbkey_hashcode NULL
-
-pn_class_t* Pn_rbkey__class(void) {
-    static pn_class_t clazz = PN_CLASS(Pn_rbkey);
-    return &clazz;
-}
-
-Pn_rbkey_t *Pn_rbkey_new(void) {
-    return (Pn_rbkey_t *) pn_class_new(Pn_rbkey__class(), sizeof(Pn_rbkey_t));
-}
-%}
-
-pn_class_t* Pn_rbkey__class(void);
-Pn_rbkey_t *Pn_rbkey_new(void);
-
-%inline %{
-
-Pn_rbkey_t *Pn_rbkey_new(void);
-
-void Pn_rbkey_set_registry(Pn_rbkey_t *rbkey, void *registry) {
-  assert(rbkey);
-  rbkey->registry = registry;
-}
-
-void *Pn_rbkey_get_registry(Pn_rbkey_t *rbkey) {
-  assert(rbkey);
-  return rbkey->registry;
-}
-
-void Pn_rbkey_set_method(Pn_rbkey_t *rbkey, char *method) {
-  assert(rbkey);
-  rbkey->method = method;
-}
-
-char *Pn_rbkey_get_method(Pn_rbkey_t *rbkey) {
-  assert(rbkey);
-  return rbkey->method;
-}
-
-void Pn_rbkey_set_key_value(Pn_rbkey_t *rbkey, char *key_value) {
-  assert(rbkey);
-  rbkey->key_value = malloc(strlen(key_value) + 1);
-  strncpy(rbkey->key_value, key_value, strlen(key_value) + 1);
-}
-
-char *Pn_rbkey_get_key_value(Pn_rbkey_t *rbkey) {
-  assert(rbkey);
-  return rbkey->key_value;
-}
-
-Pn_rbkey_t *pni_void2rbkey(void *object) {
-  return (Pn_rbkey_t *)object;
-}
-
-VALUE pn_void2rb(void *object) {
-  return (VALUE )object;
-}
-
-void *pn_rb2void(VALUE object) {
-  return (void *)object;
-}
-
-VALUE pni_address_of(void *object) {
-  return ULL2NUM((unsigned long )object);
-}
-
-%}
-
-//%rename(pn_collector_put) wrap_pn_collector_put;
-//%inline %{
-//  pn_event_t *wrap_pn_collector_put(pn_collector_t *collector, void *context,
-//                               pn_event_type_t type) {
-//    return pn_collector_put(collector, PN_RBREF, context, type);
-//  }
-//  %}
-//%ignore pn_collector_put;
-
-int pn_ssl_get_peer_hostname(pn_ssl_t *ssl, char *OUTPUT, size_t *OUTPUT_SIZE);
-%ignore pn_ssl_get_peer_hostname;
-
-%inline %{
-
-  VALUE pni_ruby_get_proton_module() {
-    VALUE mQpid = rb_define_module("Qpid");
-    return rb_define_module_under(mQpid, "Proton");
-  }
-
-  void pni_ruby_add_to_registry(VALUE key, VALUE value) {
-    VALUE result = rb_funcall(pni_ruby_get_proton_module(), rb_intern("add_to_registry"), 2, key, value);
-  }
-
-  VALUE pni_ruby_get_from_registry(VALUE key) {
-     return rb_funcall(pni_ruby_get_proton_module(), rb_intern("get_from_registry"), 1, key);
-  }
-
-  void pni_ruby_delete_from_registry(VALUE stored_key) {
-    rb_funcall(pni_ruby_get_proton_module(), rb_intern("delete_from_registry"), 1, stored_key);
-  }
-
-  typedef struct {
-    VALUE handler_key;
-  } Pni_rbhandler_t;
-
-  static Pni_rbhandler_t *pni_rbhandler(pn_handler_t *handler) {
-    return (Pni_rbhandler_t *) pn_handler_mem(handler);
-  }
-
-  static void pni_rbdispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-    Pni_rbhandler_t *rbh = pni_rbhandler(handler);
-    VALUE rbhandler = pni_ruby_get_from_registry(rbh->handler_key);
-
-    rb_funcall(rbhandler, rb_intern("dispatch"), 2, SWIG_NewPointerObj(event, SWIGTYPE_p_pn_event_t, 0), INT2FIX(type));
-  }
-
-  static void pni_rbhandler_finalize(pn_handler_t *handler) {
-    Pni_rbhandler_t *rbh = pni_rbhandler(handler);
-    pni_ruby_delete_from_registry(rbh->handler_key);
-  }
-
-  pn_handler_t *pn_rbhandler(VALUE handler) {
-    pn_handler_t *chandler = pn_handler_new(pni_rbdispatch, sizeof(Pni_rbhandler_t), pni_rbhandler_finalize);
-    Pni_rbhandler_t *rhy = pni_rbhandler(chandler);
-
-    VALUE ruby_key = rb_class_new_instance(0, NULL, rb_cObject);
-    pni_ruby_add_to_registry(ruby_key, handler);
-
-    rhy->handler_key = ruby_key;
-
-    return chandler;
-  }
-
-%}
-
-%include "proton/cproton.i"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/bindings/ruby/tests/test_connection_driver.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_connection_driver.rb b/proton-c/bindings/ruby/tests/test_connection_driver.rb
new file mode 100644
index 0000000..2ddc8ef
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/test_connection_driver.rb
@@ -0,0 +1,70 @@
+#--
+# 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
+#
+# 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.
+#++
+
+require 'test_tools'
+
+include Qpid::Proton
+
+class ConnectionDriverTest < Minitest::Test
+
+  def setup
+    @sockets = Socket.pair(:LOCAL, :STREAM, 0)
+  end
+
+  def test_send_recv
+    send_class = Class.new(MessagingHandler) do
+      attr_reader :accepted
+      def on_sendable(event) event.sender.send Message.new("foo"); end
+      def on_accepted(event) event.connection.close; @accepted = true; end
+    end
+
+    recv_class = Class.new(MessagingHandler) do
+      attr_reader :message
+      def on_link_opened(event) event.link.flow(1); event.link.open; end
+      def on_message(event) @message = event.message; event.connection.close; end
+    end
+
+    sender = ConnectionDriver.new(@sockets[0], send_class.new)
+    sender.connection.open();
+    sender.connection.open_sender()
+
+    receiver = ConnectionDriver.new(@sockets[1], recv_class.new)
+    drivers = [sender, receiver]
+    until drivers.all? { |d| d.finished? }
+      rd = drivers.select {|d| d.can_read? }
+      wr = drivers.select {|d| d.can_write? }
+      rs, ws = IO.select(rd, wr)
+      ws.each { |d| d.write; d.dispatch }
+      rs.each { |d| d.read; d.dispatch }
+    end
+    assert_equal(receiver.handler.message.body, "foo")
+    assert(sender.handler.accepted)
+  end
+
+  def test_idle
+    idle_class = Class.new(MessagingHandler) do
+      def on_connection_bound(event) event.transport.idle_timeout = 10; end
+    end
+    drivers = [ConnectionDriver.new(@sockets[0], idle_class.new), ConnectionDriver.new(@sockets[1])]
+    drivers[0].connection.open()
+    now = Time.now
+    drivers.each { |d| d.process(true, true, now) } until drivers[0].connection.open?
+    assert_equal(10, drivers[0].transport.idle_timeout)
+    assert_in_delta(10, (drivers[0].tick(now) - now)*1000, 1)
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/9bb1baad/proton-c/include/proton/cproton.i
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/cproton.i b/proton-c/include/proton/cproton.i
index 931437e..5f375de 100644
--- a/proton-c/include/proton/cproton.i
+++ b/proton-c/include/proton/cproton.i
@@ -1053,7 +1053,18 @@ typedef unsigned long int uintptr_t;
   pn_selectable_t *pn_cast_pn_selectable(void *x) { return (pn_selectable_t *) x; }
 %}
 
-%include "proton/url.h"
+/* Connection driver */
+%{
+#include <proton/connection_driver.h>
+%}
+/* Don't wrap the pn_connection_driver_t struct, just the functions */
+%ignore pn_connection_driver_t;
+%ignore pn_connection_driver_verrorf;
+%ignore pn_connection_driver_logf;
+%ignore pn_connection_driver_vlogf;
+%include "proton/connection_driver.h"
 
+
+%include "proton/url.h"
 %include "proton/reactor.h"
 %include "proton/handlers.h"


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


[05/10] qpid-proton git commit: PROTON-1064: [ruby] remove obsolete examples

Posted by ac...@apache.org.
PROTON-1064: [ruby] remove obsolete examples

- make to_native_path available to bindings
- move Container examples to top level
- Update INSTALL.md doc
- Add ruby-docs target to generate docs with yard


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

Branch: refs/heads/master
Commit: 3d258158c6e5f95e8a6123a3faddf89772b563ce
Parents: bca7fd1
Author: Alan Conway <ac...@redhat.com>
Authored: Fri Sep 15 10:47:03 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 INSTALL.md                                      |   6 +-
 examples/ruby/README.md                         | 103 +++++
 examples/ruby/broker.rb                         | 198 ++++++++++
 examples/ruby/client.rb                         |  82 ++++
 examples/ruby/direct_recv.rb                    |  66 ++++
 examples/ruby/direct_send.rb                    |  76 ++++
 examples/ruby/engine_recv.rb                    | 158 --------
 examples/ruby/engine_send.rb                    | 143 -------
 examples/ruby/example_test.rb                   |  41 +-
 examples/ruby/helloworld.rb                     |  73 ++++
 examples/ruby/helloworld_direct.rb              |  74 ++++
 examples/ruby/lib/debugging.rb                  |  26 --
 examples/ruby/lib/driver.rb                     |  69 ----
 examples/ruby/lib/qpid_examples.rb              |  28 --
 examples/ruby/lib/selectable.rb                 | 120 ------
 examples/ruby/lib/send_and_receive.rb           |  90 -----
 examples/ruby/reactor/README.md                 | 103 -----
 examples/ruby/reactor/broker.rb                 | 200 ----------
 examples/ruby/reactor/client.rb                 |  82 ----
 examples/ruby/reactor/direct_recv.rb            |  60 ---
 examples/ruby/reactor/direct_send.rb            |  59 ---
 examples/ruby/reactor/helloworld.rb             |  73 ----
 examples/ruby/reactor/helloworld_direct.rb      |  74 ----
 examples/ruby/reactor/server.rb                 |  76 ----
 examples/ruby/reactor/simple_recv.rb            |  58 ---
 examples/ruby/reactor/simple_send.rb            |  55 ---
 examples/ruby/registry_test.rb                  |  76 ----
 examples/ruby/server.rb                         |  76 ++++
 examples/ruby/simple_recv.rb                    |  69 ++++
 examples/ruby/simple_send.rb                    |  73 ++++
 examples/ruby/wrapper_test.rb                   |  82 ----
 proton-c/CMakeLists.txt                         |  15 +
 proton-c/bindings/ruby/lib/core/message.rb      |  10 +-
 .../bindings/ruby/lib/messenger/messenger.rb    |   2 +
 proton-c/bindings/ruby/spec/messenger_spec.rb   | 393 -------------------
 proton-c/bindings/ruby/tests/test_tools.rb      |   8 +-
 36 files changed, 958 insertions(+), 2039 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/INSTALL.md
----------------------------------------------------------------------
diff --git a/INSTALL.md b/INSTALL.md
index b9482ff..0458bd2 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -49,8 +49,10 @@ language.
     $ yum install php-devel                                  # PHP
     $ yum install perl-devel                                 # Perl
 
-    # Dependencies needed for Python docs
-    $ yum install epydoc
+    # Dependencies needed to generate documentation
+    $ yum install epydoc                                     # Python
+    $ yum install rubygem-yard                               # Ruby
+    $ yum install doxygen                                    # C, C++
 
 The following prerequisites are required to do a full build on
 Debian-based systems (Ubuntu).  If you do not wish to build a given

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/README.md
----------------------------------------------------------------------
diff --git a/examples/ruby/README.md b/examples/ruby/README.md
new file mode 100644
index 0000000..38cc6ba
--- /dev/null
+++ b/examples/ruby/README.md
@@ -0,0 +1,103 @@
+## What Is The Reactor?
+
+A little outside of the scope of this document, but the reactor is an event source for letting an application know about events in the Proton messaging system. With this set of APIs an application can be register handlers that are notified when a connection is created, a message received, or a session closes.
+
+### Handlers
+
+An application creates **handlers**, objects which provide methods through which the reactor notifies the application's components of events and allows them each to handle the ones in which they are interested (see the Chain Of Responsibility design pattern for more on this idea). There are some pre-defined handlers for responding to incoming message events, outgoing message events, data flow and managing the AMQP endpoints. Look in the **Qpid::Proton::Handlers** package for more details on these classes.
+
+## Simple Reactor Examples
+
+### The Broker
+
+The reactor examples come with a sample broker which can be used by other examples and which also works as an example itself. For now we'll just start up the broker example and tell it to listen on port 8888:
+
+````
+$ ruby ../examples/ruby/reactor/broker.rb  --address=0.0.0.0:8888
+Listening on 0.0.0.0:8888
+````
+
+This example broker will receive messages, create queues as needed, and deliver messages to endpoints.
+
+### Hello World Using A Broker
+
+Our first example creates an endpoint that sends messages to a queue to which it is subscribed. So it both sends and receives its message in one pass.
+
+To start it, simply run:
+
+```
+$ ruby ../examples/ruby/reactor/helloworld.rb --address=0.0.0.0:8888 --queue=examples
+Hello world!
+```
+
+As you can see, the classic message was output by the example. Now let's take a look at what's going on under the covers.
+
+#### Events When Talking To A Broker
+
+The following events occur while **helloworld.rb** runs:
+
+ * **on_start** - Fired when the application is started.
+ * **on_sendable** - Fired when a message can be sent.
+ * **on_message** - Fired when a message is received.
+
+### Hello World Without A Broker required
+
+The next example we'll look at will send the classic "Hello world" message to itself directly. This example shows some very fundamental elements of the reactor APIs that you should understand.
+
+To launch the example:
+
+```
+ $ ruby helloworld_direct.rb --address=0.0.0.0:8888/examples
+ Hello world!
+```
+
+Not very different from the example that uses the broker, which is what we'd expect from the outside. But let's take a look inside of the example and see how it's different at that level
+
+The direct version takes on the responsibility for listening to incoming connections as well as making an outgoing connection. So we see the following additional events occurring:
+
+ * **on_accepted** - Fired when a message is received.
+ * **on_connection_closed** - Fired when an endpoint closes its connection.
+
+## More Complex Reactor Examples
+
+Now that we've covered the basics with the archetypical hello world app, let's look at some more interesting examples.
+
+There are four example applications that demonstrate how to send and receive messages both directly and through an intermediary, such as a broker:
+
+ * **simple_send.rb** - sends messages to a receiver at a specific address and receives responses via an intermediary,
+ * **simple_recv.rb** - receives messages from via an intermediary,
+ * **direct_send.rb** - sends messages directly to a receiver and listens for responses itself, and
+ * **direct_recv.rb** - receives messages directly.
+
+ Simple send and direct send may, at first, seem to be so similar that you wonder why they're not just the same applciation. And I know for me I was wonder as I wrote the list above why there were two examples. The reason is that **simple_send.rb** uses the intermediary transfer responses to the messages it sends, while **direct_send.rb** uses an *Acceptor* to listen for an process responses.
+
+ You can use the examples in the follow ways:
+
+ ```
+ simple_send.rb -> broker <- simple_recv.rb
+ simple_send.rb -> direct_recv.rb
+ direct_send.rb -> simple_recv.rb
+ ```
+
+In this set of examples we see the following event occurring, in addition to what we've seen before:
+
+ * **on_disconnected** - Fired when the transport is closed.
+
+## Now About That Broker example
+
+The **broker.rb** example application is a nice demonstration of doing something more interesting in Ruby with Proton.
+
+The way the broker works is to listen to incoming connections, examine the components of the address for that connection, attach that connection to an exchange managing that address and then it sends any messages destined for that address to them.
+
+The components of the broker example include:
+ * **Broker** - A class that extends the MessagingHandler class. It accepts incoming connections, manages subscribing them to exchanges, and transfers messages between them.
+ * **Exchange** - A class that represents a message queue, tracking what endpoints are subscribed to it.
+
+The Broker manages a map connecting a queue address to the instance of Exchange that holds references to the endpoints of interest.
+
+The broker application demonstrates a new set of reactor events:
+
+ * **on_link_opening** - Fired when a remote link is opened but the local end is not yet open. From this event the broker grabs the address and subscribes the link to an exchange for that address.
+ * **on_link_closing** - Fired when a remote link is closed but the local end is still open. From this event the broker grabs the address and unsubscribes the link from that exchange.
+ * **on_connection_closing** - Fired when a remote connection is closed but the local end is still open.
+ * **on_disconnected** - Fired when the protocol transport has closed. The broker removes all links for the disconnected connection, avoiding workign with endpoints that are now gone.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/broker.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/broker.rb b/examples/ruby/broker.rb
new file mode 100644
index 0000000..2c37a39
--- /dev/null
+++ b/examples/ruby/broker.rb
@@ -0,0 +1,198 @@
+#--
+# 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.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+require 'pathname'
+
+def debug(text)
+  print "[#{Time.now.strftime('%s')}] #{text}\n" if $options[:debug]
+end
+
+class Exchange
+
+  def initialize(dynamic = false)
+    @dynamic = dynamic
+    @queue = Queue.new
+    @consumers = []
+  end
+
+  def subscribe(consumer)
+    debug("subscribing #{consumer}")
+    @consumers << (consumer)
+    debug(" there are #{@consumers.size} consumers")
+  end
+
+  def unsubscribe(consumer)
+    debug("unsubscribing #{consumer}")
+    if @consumers.include?(consumer)
+      @consumers.delete(consumer)
+    else
+      debug(" consumer doesn't exist")
+    end
+    debug("  there are #{@consumers.size} consumers")
+    @consumers.empty? && (@dynamic || @queue.empty?)
+  end
+
+  def publish(message)
+    debug("queueing message: #{message.body}")
+    @queue << message
+    self.dispatch
+  end
+
+  def dispatch(consumer = nil)
+    debug("dispatching: consumer=#{consumer}")
+    if consumer
+      c = [consumer]
+    else
+      c = @consumers
+    end
+
+    while self.deliver_to(c) do
+    end
+  end
+
+  def deliver_to(consumers)
+    debug("delivering to #{consumers.size} consumer(s)")
+    result = false
+    consumers.each do |consumer|
+      debug(" current consumer=#{consumer} credit=#{consumer.credit}")
+      if consumer.credit > 0 && !@queue.empty?
+        consumer.send(@queue.pop(true))
+        result = true
+      end
+    end
+    return result
+  end
+
+end
+
+class Broker < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url)
+    super()
+    @url = url
+    @queues = {}
+  end
+
+  def on_start(event)
+    debug("on_start event")
+    @acceptor = event.container.listen(@url)
+    print "Listening on #{@url}\n"
+  end
+
+  def queue(address)
+    debug("fetching queue for #{address}: (there are #{@queues.size} queues)")
+    unless @queues.has_key?(address)
+      debug(" creating new queue")
+      @queues[address] = Exchange.new
+    else
+      debug(" using existing queue")
+    end
+    result = @queues[address]
+    debug(" returning #{result}")
+    return result
+  end
+
+  def on_link_opening(event)
+    debug("processing on_link_opening")
+    debug("link is#{event.link.sender? ? '' : ' not'} a sender")
+    if event.link.sender?
+      if event.link.remote_source.dynamic?
+        address = SecureRandom.uuid
+        event.link.source.address = address
+        q = Exchange.new(true)
+        @queues[address] = q
+        q.subscribe(event.link)
+      elsif event.link.remote_source.address
+        event.link.source.address = event.link.remote_source.address
+        self.queue(event.link.source.address).subscribe(event.link)
+      end
+    elsif event.link.remote_target.address
+      event.link.target.address = event.link.remote_target.address
+    end
+  end
+
+  def unsubscribe(link)
+    debug("unsubscribing #{link.source.address}")
+    if @queues.has_key?(link.source.address)
+      if @queues[link.source.address].unsubscribe(link)
+        @queues.delete(link.source.address)
+      end
+    end
+  end
+
+  def on_link_closing(event)
+    self.unsubscribe(event.link) if event.link.sender?
+  end
+
+  def on_connection_closing(event)
+    self.remove_stale_consumers(event.connection)
+  end
+
+  def on_disconnected(event)
+    self.remove_stale_consumers(event.connection)
+  end
+
+  def remove_stale_consumers(connection)
+    l = connection.link_head(Qpid::Proton::Endpoint::REMOTE_ACTIVE)
+    while !l.nil?
+      self.unsubscribe(l) if l.sender?
+      l = l.next(Qpid::Proton::Endpoint::REMOTE_ACTIVE)
+    end
+  end
+
+  def on_sendable(event)
+    debug("on_sendable event")
+    q = self.queue(event.link.source.address)
+    debug(" dispatching #{event.message} to #{q}")
+    q.dispatch(event.link)
+  end
+
+  def on_message(event)
+    debug("on_message event")
+    q = self.queue(event.link.target.address)
+    debug(" dispatching #{event.message} to #{q}")
+    q.publish(event.message)
+  end
+
+end
+
+$options = {
+  :address => "localhost:5672",
+  :debug => false
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: #{Pathname.new(__FILE__).basename} [$options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{$options[:address]}).") do |address|
+    $options[:address] = address
+  end
+
+  opts.on("-d", "--debug", "Enable debugging output (def. #{$options[:debug]})") do
+    $options[:debug] = true
+  end
+
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(Broker.new($options[:address])).run
+rescue Interrupt
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/client.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/client.rb b/examples/ruby/client.rb
new file mode 100644
index 0000000..8c38f38
--- /dev/null
+++ b/examples/ruby/client.rb
@@ -0,0 +1,82 @@
+#--
+# 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.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class Client < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url, requests)
+    super()
+    @url = url
+    @requests = requests
+  end
+
+  def on_start(event)
+    @sender = event.container.create_sender(@url)
+    @receiver = event.container.create_receiver(@sender.connection, :dynamic => true)
+  end
+
+  def next_request
+    if @receiver.remote_source.address
+      req = Qpid::Proton::Message.new
+      req.reply_to = @receiver.remote_source.address
+      req.body = @requests.first
+      puts "-> #{req.body}"
+      @sender.send(req)
+    end
+  end
+
+  def on_link_opened(event)
+    if event.receiver == @receiver
+      next_request
+    end
+  end
+
+  def on_message(event)
+    puts "<- #{event.message.body}"
+    @requests.delete_at(0)
+    if !@requests.empty?
+      next_request
+    else
+      event.connection.close
+    end
+  end
+
+  def on_transport_error(event)
+    raise "Connection error: #{event.transport.condition}"
+  end
+
+end
+
+REQUESTS = ["Twas brillig, and the slithy toves",
+            "Did gire and gymble in the wabe.",
+            "All mimsy were the borogroves,",
+            "And the mome raths outgrabe."]
+
+options = {
+  :address => "localhost:5672/examples",
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: client.rb [options]"
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(Client.new(options[:address], REQUESTS)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/direct_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/direct_recv.rb b/examples/ruby/direct_recv.rb
new file mode 100644
index 0000000..411efba
--- /dev/null
+++ b/examples/ruby/direct_recv.rb
@@ -0,0 +1,66 @@
+#--
+# 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.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class DirectReceive < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url, expected)
+    super()
+    @url = url
+    @expected = expected
+    @received = 0
+  end
+
+  def on_start(event)
+    @acceptor = event.container.listen(@url)
+  end
+
+  def on_message(event)
+    if @expected.zero? || (@received < @expected)
+      puts "Received: #{event.message.body}"
+      @received = @received + 1
+      if @received == @expected
+        event.connection.close
+        @acceptor.close
+      end
+    end
+  end
+end
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(DirectReceive.new(options[:address], options[:messages])).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/direct_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/direct_send.rb b/examples/ruby/direct_send.rb
new file mode 100644
index 0000000..ed679aa
--- /dev/null
+++ b/examples/ruby/direct_send.rb
@@ -0,0 +1,76 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+class SimpleSend < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url, expected)
+    super()
+    @url = url
+    @sent = 0
+    @confirmed = 0
+    @expected = expected
+  end
+
+  def on_start(event)
+    @acceptor = event.container.listen(@url)
+  end
+
+  def on_sendable(event)
+    while event.sender.credit > 0 && @sent < @expected
+      msg = Qpid::Proton::Message.new("sequence #{@sent}", { :id => @sent } )
+      event.sender.send(msg)
+      @sent = @sent + 1
+    end
+  end
+
+  def on_accepted(event)
+    @confirmed = @confirmed + 1
+    if @confirmed == @expected
+      puts "All #{@expected} messages confirmed!"
+      event.connection.close
+    end
+  end
+end
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run
+rescue Interrupt => error
+  puts "ERROR: #{error}"
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/engine_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/engine_recv.rb b/examples/ruby/engine_recv.rb
deleted file mode 100644
index 1529964..0000000
--- a/examples/ruby/engine_recv.rb
+++ /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.
-#++
-
-require "qpid_examples"
-require "optparse"
-
-DEFAULT_PORT = 5672
-
-options = {
-  :port => DEFAULT_PORT,
-  :debug => false,
-  :verbose => false,
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: engine_recv.rb [options]"
-
-  opts.on("-p [port]", "--port [port]",
-          "The port to use  (def. #{DEFAULT_PORT})") do |port|
-    options[:port] = port
-  end
-
-  opts.on("-v", "--verbose",
-          "Enable verbose output") do
-    options[:verbose] = true
-  end
-
-  opts.on("-d",
-          "--debug", "Enable debugging") do
-    options[:debug] = true
-  end
-
-  opts.parse!
-end
-
-server = TCPServer.new('localhost', options[:port])
-
-last_time = Time.now
-
-message_count = 0
-driver = Driver.new
-
-collector = Qpid::Proton::Event::Collector.new
-
-loop do
-  begin
-    client = server.accept_nonblock
-  rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR, Errno::EWOULDBLOCK => error
-
-  end
-
-  unless client.nil?
-    puts "Connection from #{client.peeraddr.last}"
-    connection = Qpid::Proton::Connection.new
-    connection.collect(collector)
-    transport = Qpid::Proton::Transport.new(Qpid::Proton::Transport::SERVER)
-    transport.bind(connection)
-    selectable = Selectable.new(transport, client)
-    driver.add(selectable)
-  end
-
-  # let the driver process data
-  driver.process
-
-  event = collector.peek
-
-  while !event.nil?
-    puts "EVENT: #{event}" if options[:debug]
-
-    case event.type
-    when Qpid::Proton::Event::CONNECTION_INIT
-      conn = event.connection
-      if conn.state & Qpid::Proton::Endpoint::REMOTE_UNINIT
-        conn.transport.sasl.done(Qpid::Proton::SASL::OK)
-      end
-
-    when Qpid::Proton::Event::CONNECTION_BOUND
-      conn = event.connection
-      if conn.state & Qpid::Proton::Endpoint::LOCAL_UNINIT
-        conn.open
-      end
-
-    when Qpid::Proton::Event::CONNECTION_REMOTE_CLOSE
-      conn = event.context
-      if !(conn.state & Qpid::Proton::Endpoint::LOCAL_CLOSED)
-        conn.close
-      end
-
-    when Qpid::Proton::Event::SESSION_REMOTE_OPEN
-      session = event.session
-      if session.state & Qpid::Proton::Endpoint::LOCAL_UNINIT
-        session.incoming_capacity = 1000000
-        session.open
-      end
-
-    when Qpid::Proton::Event::SESSION_REMOTE_CLOSE
-      session = event.session
-      if !(session.state & Qpid::Proton::Endpoint::LOCAL_CLOSED)
-        session.close
-      end
-
-    when Qpid::Proton::Event::LINK_REMOTE_OPEN
-      link = event.link
-      if link.state & Qpid::Proton::Endpoint::LOCAL_UNINIT
-        link.open
-        link.flow 400
-      end
-
-    when Qpid::Proton::Event::LINK_REMOTE_CLOSE
-      link = event.context
-      if !(link.state & Qpid::Proton::Endpoint::LOCAL_CLOSED)
-        link.close
-      end
-
-    when Qpid::Proton::Event::DELIVERY
-      link = event.link
-      delivery = event.delivery
-      if delivery.readable? && !delivery.partial?
-        # decode the message and display it
-        msg = Qpid::Proton::Util::Engine.receive_message(delivery)
-        message_count += 1
-        puts "Received:"
-        puts " Count=#{message_count}" if options[:verbose]
-        puts " From=#{msg.id}" if msg.id
-        puts " Reply to=#{msg.reply_to}" if msg.reply_to
-        puts " Subject=#{msg.subject}" if msg.subject
-        puts " Body=#{msg.body}" if msg.body
-        puts ""
-        delivery.settle
-        credit = link.credit
-        link.flow(200) if credit <= 200
-      end
-
-    when Qpid::Proton::Event::TRANSPORT
-      driver.process
-
-    end
-
-    collector.pop
-    event = collector.peek
-  end
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/engine_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/engine_send.rb b/examples/ruby/engine_send.rb
deleted file mode 100644
index 189c7fd..0000000
--- a/examples/ruby/engine_send.rb
+++ /dev/null
@@ -1,143 +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.
-#++
-
-require 'qpid_examples'
-require "optparse"
-
-DEFAULT_ADDRESS = "0.0.0.0:5672"
-
-options = {
-  :address => DEFAULT_ADDRESS,
-  :debug => false,
-  :verbose => false,
-  :count => 1,
-  :content => "This message was sent #{Time.new}"
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: engine_recv.rb [options]"
-
-  opts.on("-a [address]", "--address [address]",
-          "The target address (def. #{DEFAULT_ADDRESS})") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-C [content]", "--content [content]",
-          "The message content") do |content|
-    options[:content] = content
-  end
-
-  opts.on("-c [count]", "--count [count]",
-          "The number of messages to send (def. 1)") do |count|
-    options[:count] = count.to_i
-  end
-
-  opts.on("-v", "--verbose",
-          "Enable verbose output") do
-    options[:verbose] = true
-  end
-
-  opts.on("-d",
-          "--debug", "Enable debugging") do
-    options[:debug] = true
-  end
-
-  opts.parse!
-end
-
-
-driver = Driver.new
-
-conn = Qpid::Proton::Connection.new
-collector = Qpid::Proton::Event::Collector.new
-conn.collect(collector)
-
-session = conn.session
-conn.open
-session.open
-
-sender = session.sender("tvc_15_1")
-sender.target.address = "queue"
-sender.open
-
-transport = Qpid::Proton::Transport.new
-transport.bind(conn)
-
-address, port = options[:address].split(":")
-
-socket = TCPSocket.new(address, port)
-selectable = Selectable.new(transport, socket)
-sent_count = 0
-
-sent_count = 0
-
-driver.add(selectable)
-
-loop do
-  # let the driver process
-  driver.process
-
-  event = collector.peek
-
-  unless event.nil?
-
-    print "EVENT: #{event}\n" if options[:debug]
-
-    case event.type
-
-    when Qpid::Proton::Event::LINK_FLOW
-      sender = event.sender
-      credit = sender.credit
-
-      message = Qpid::Proton::Message.new
-
-      if credit > 0 && sent_count < options[:count]
-        sent_count = sent_count.next
-        message.clear
-        message.address = options[:address]
-        message.subject = "Message #{sent_count}..."
-        message.body = options[:content]
-
-        delivery = sender.delivery("#{sent_count}")
-        sender.send(message.encode)
-        delivery.settle
-        sender.advance
-        credit = sender.credit
-      else
-        sender.close
-      end
-
-    when Qpid::Proton::Event::LINK_LOCAL_CLOSE
-      link = event.link
-      link.close
-      link.session.close
-
-    when Qpid::Proton::Event::SESSION_LOCAL_CLOSE
-      session = event.session
-      session.connection.close
-
-    when Qpid::Proton::Event::CONNECTION_LOCAL_CLOSE
-      break
-
-    end
-
-    collector.pop
-    event = collector.peek
-  end
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/example_test.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/example_test.rb b/examples/ruby/example_test.rb
index 86890ce..12ab784 100755
--- a/examples/ruby/example_test.rb
+++ b/examples/ruby/example_test.rb
@@ -25,26 +25,53 @@ require 'socket'
 class ExampleTest < MiniTest::Test
 
   def run_script(script, port)
-    assert File.exist? script
     cmd = [RbConfig.ruby, script]
     cmd += ["-a", ":#{port}/examples"] if port
     return IO.popen(cmd)
   end
 
-
   def assert_output(script, want, port=nil)
     out = run_script(script, port)
     assert_equal want, out.read.strip
   end
 
   def test_helloworld
-    assert_output("reactor/helloworld.rb", "Hello world!", $port)
+    assert_output("helloworld.rb", "Hello world!", $port)
   end
 
   def test_send_recv
-    assert_output("reactor/simple_send.rb", "All 100 messages confirmed!", $port)
+    assert_output("simple_send.rb", "All 100 messages confirmed!", $port)
     want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
-    assert_output("reactor/simple_recv.rb", want.strip, $port)
+    assert_output("simple_recv.rb", want.strip, $port)
+  end
+
+  def test_direct_recv
+    TestPort.new do |tp|
+      p = run_script("direct_recv.rb", tp.port)
+      wait_port tp.port
+      assert_output("simple_send.rb", "All 100 messages confirmed!", tp.port)
+      want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+      assert_equal(want.strip, p.read.strip)
+    end
+  end
+
+  def test_direct_send
+    TestPort.new do |tp|
+      p = run_script("direct_send.rb", tp.port)
+      wait_port tp.port
+      want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+      assert_output("simple_recv.rb", want.strip, $port)
+      assert_equal("All 100 messages confirmed!", p.read.strip)
+    end
+  end
+
+  def test_direct_send
+    TestPort.start_wait do |port|
+      p = run_script("direct_recv.rb", port)
+      assert_output("simple_send.rb", "All 100 messages confirmed!", port)
+      want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+      assert_equal(want.strip, p.read.strip)
+    end
   end
 
   def test_client_server
@@ -58,8 +85,8 @@ class ExampleTest < MiniTest::Test
 -> And the mome raths outgrabe.
 <- AND THE MOME RATHS OUTGRABE.
 EOS
-    srv = run_script("reactor/server.rb", $port)
-    assert_output("reactor/client.rb", want.strip, $port)
+    srv = run_script("server.rb", $port)
+    assert_output("client.rb", want.strip, $port)
 
   ensure
     Process.kill :TERM, srv.pid if srv

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/helloworld.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/helloworld.rb b/examples/ruby/helloworld.rb
new file mode 100644
index 0000000..9b02e8a
--- /dev/null
+++ b/examples/ruby/helloworld.rb
@@ -0,0 +1,73 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class HelloWorld < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(server, address)
+    super()
+    @server = server
+    @address = address
+  end
+
+  def on_start(event)
+    conn = event.container.connect(:address => @server)
+    event.container.create_sender(conn, :target => @address)
+    event.container.create_receiver(conn, :source => @address)
+  end
+
+  def on_sendable(event)
+    msg = Qpid::Proton::Message.new
+    msg.body = "Hello world!"
+    event.sender.send(msg)
+    event.sender.close
+  end
+
+  def on_message(event)
+    puts event.message.body
+    event.connection.close
+  end
+
+  def on_transport_error(event)
+    raise "Connection error: #{event.transport.condition}"
+  end
+end
+
+options = {
+  :address => "localhost:5672",
+  :queue => "examples"
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: helloworld_direct.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-q", "--queue=QUEUE", "Send messages to QUEUE (def. #{options[:queue]})") do |queue|
+    options[:queue] = queue
+  end
+
+end.parse!
+
+hw = HelloWorld.new(options[:address], "examples")
+Qpid::Proton::Reactor::Container.new(hw).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/helloworld_direct.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/helloworld_direct.rb b/examples/ruby/helloworld_direct.rb
new file mode 100644
index 0000000..e98cc1f
--- /dev/null
+++ b/examples/ruby/helloworld_direct.rb
@@ -0,0 +1,74 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+options = {
+  :address => "localhost:5672/examples",
+}
+
+class HelloWorldDirect < Qpid::Proton::Handler::MessagingHandler
+
+  include Qpid::Proton::Util::Wrapper
+
+  def initialize(url)
+    super()
+    @url = url
+  end
+
+  def on_start(event)
+    @acceptor = event.container.listen(@url)
+    event.container.create_sender(@url)
+  end
+
+  def on_sendable(event)
+    msg = Qpid::Proton::Message.new
+    msg.body = "Hello world!"
+    event.sender.send(msg)
+    event.sender.close
+  end
+
+  def on_message(event)
+    puts "#{event.message.body}"
+  end
+
+  def on_accepted(event)
+    event.connection.close
+  end
+
+  def on_connection_closed(event)
+    @acceptor.close
+  end
+
+end
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: helloworld_direct.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(HelloWorldDirect.new(options[:address])).run
+rescue Interrupt => error
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/lib/debugging.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/lib/debugging.rb b/examples/ruby/lib/debugging.rb
deleted file mode 100644
index 5065d51..0000000
--- a/examples/ruby/lib/debugging.rb
+++ /dev/null
@@ -1,26 +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.
-#++
-
-module Debugging
-
-  def debug(text)
-    print "[#{Time.now.strftime('%s')}] #{text}\n"
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/lib/driver.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/lib/driver.rb b/examples/ruby/lib/driver.rb
deleted file mode 100644
index 4e223d0..0000000
--- a/examples/ruby/lib/driver.rb
+++ /dev/null
@@ -1,69 +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.
-#++
-
-class Driver
-
-  def initialize
-    @selectables = {}
-  end
-
-  def add(selectable)
-    @selectables[selectable.fileno] = selectable
-  end
-
-  def process
-    reading = []
-    writing = []
-
-    @selectables.each_value do |sel|
-      if sel.closed? || sel.fileno.nil?
-        @selectables.delete(sel.fileno)
-      else
-        begin
-          reading << sel.to_io if sel.reading?
-          writing << sel.to_io if sel.writing?
-        rescue Exception => error
-          puts "Error: #{error}"
-          puts error.backtrace.join("\n");
-          # @selectables.delete(sel.fileno)
-        end
-      end
-    end
-
-    read_from, write_to = IO.select(reading, writing, [], 0)
-
-    unless read_from.nil?
-      read_from.each do |r|
-        sel = @selectables[r.fileno]
-        sel.readable unless sel.nil? || sel.closed?
-      end
-    end
-
-    begin
-      unless write_to.nil?
-        write_to.each do |w|
-          sel = @selectables[w.fileno]
-          sel.writable unless sel.nil? || sel.closed?
-        end
-      end
-
-    end
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/lib/qpid_examples.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/lib/qpid_examples.rb b/examples/ruby/lib/qpid_examples.rb
deleted file mode 100644
index 8503fbe..0000000
--- a/examples/ruby/lib/qpid_examples.rb
+++ /dev/null
@@ -1,28 +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.
-#++
-
-require "qpid_proton"
-
-require "selectable"
-require "driver"
-require "socket"
-require "monitor"
-
-include Socket::Constants
-

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/lib/selectable.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/lib/selectable.rb b/examples/ruby/lib/selectable.rb
deleted file mode 100644
index 779ea24..0000000
--- a/examples/ruby/lib/selectable.rb
+++ /dev/null
@@ -1,120 +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.
-#++
-
-class Selectable
-
-  attr_reader :transport
-
-  def initialize(transport, socket)
-    @transport = transport
-    @socket = socket
-    @socket.autoclose = true
-    @write_done = false
-    @read_done = false
-  end
-
-  def closed?
-    return true if @socket.closed?
-    return false if !@read_done && !@write_done
-    @socket.close
-    true
-  end
-
-  def fileno
-    @socket.fileno unless @socket.closed?
-  end
-
-  def to_io
-    @socket
-  end
-
-  def reading?
-    return false if @read_done
-    c = @transport.capacity
-    if c > 0
-      return true
-    elsif c < 0
-      @read_done = true
-      return false
-    else
-      return false
-    end
-  end
-
-  def writing?
-    return false if @write_done
-    begin
-      p = @transport.pending
-      if p > 0
-        return true
-      elsif p < 0
-        @write_done = true
-        return false
-      else
-        return false
-      end
-    rescue Qpid::Proton::TransportError => error
-      @write_done = true
-      return false
-    end
-  end
-
-  def readable
-    c = @transport.capacity
-    if c > 0
-      begin
-        data = @socket.recv(c)
-        if data
-          @transport.push(data)
-        else
-          @transport.close_tail
-        end
-      rescue Exception => error
-        puts "read error; #{error}"
-        @transport.close_tail
-        @read_done = true
-      end
-    elsif c < 0
-      @read_done = true
-    end
-  end
-
-  def writable
-    begin
-      p = @transport.pending
-      if p > 0
-        data = @transport.peek(p)
-        n = @socket.send(data, 0)
-        @transport.pop(n)
-      elsif p < 0
-        @write_done = true
-      end
-    rescue Exception => error
-      puts "write error: #{error}"
-      puts error.backtrace.join("\n")
-      @transport.close_head
-      @write_done = true
-    end
-  end
-
-  def tick(now)
-    @transport.tick(now)
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/lib/send_and_receive.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/lib/send_and_receive.rb b/examples/ruby/lib/send_and_receive.rb
deleted file mode 100644
index 9fd7417..0000000
--- a/examples/ruby/lib/send_and_receive.rb
+++ /dev/null
@@ -1,90 +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.
-#++
-
-class ExampleSend < Qpid::Proton::Handler::MessagingHandler
-
-  attr_reader :url
-
-  def initialize(url, expected)
-    super()
-    @url = url
-    @sent = 0
-    @confirmed = 0
-    @expected = expected
-  end
-
-  def on_sendable(event)
-    while event.sender.credit > 0 && @sent < @expected
-      msg = Qpid::Proton::Message.new
-      msg.body = "sequence #{@sent}"
-      msg.id = @sent
-      event.sender.send(msg)
-      @sent = @sent + 1
-    end
-  end
-
-  def on_accepted(event)
-    @confirmed = @confirmed + 1
-    if self.finished?
-      puts "#{@expected > 1 ? 'All ' : ''}#{@expected} message#{@expected > 1 ? 's' : ''} confirmed!"
-      event.connection.close
-    end
-  end
-
-  def on_disconnected(event)
-    @sent = @confirmed
-  end
-
-  def finished?
-    @confirmed == @expected
-  end
-
-end
-
-class ExampleReceive < Qpid::Proton::Handler::MessagingHandler
-
-  attr_reader :url
-
-  def initialize(url, expected)
-    super()
-    @url = url
-    @expected = expected
-    @received = 0
-  end
-
-  def on_message(event)
-    if event.message.id.nil? || event.message.id < @received
-      puts "Missing or old message id: id=#{event.message.id}"
-      return
-    end
-    if @expected.zero? || (@received < @expected)
-      puts "Received: #{event.message.body}"
-      @received = @received + 1
-      if finished?
-        event.receiver.close
-        event.connection.close
-      end
-    end
-  end
-
-  def finished?
-    @received == @expected
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/README.md
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/README.md b/examples/ruby/reactor/README.md
deleted file mode 100644
index 38cc6ba..0000000
--- a/examples/ruby/reactor/README.md
+++ /dev/null
@@ -1,103 +0,0 @@
-## What Is The Reactor?
-
-A little outside of the scope of this document, but the reactor is an event source for letting an application know about events in the Proton messaging system. With this set of APIs an application can be register handlers that are notified when a connection is created, a message received, or a session closes.
-
-### Handlers
-
-An application creates **handlers**, objects which provide methods through which the reactor notifies the application's components of events and allows them each to handle the ones in which they are interested (see the Chain Of Responsibility design pattern for more on this idea). There are some pre-defined handlers for responding to incoming message events, outgoing message events, data flow and managing the AMQP endpoints. Look in the **Qpid::Proton::Handlers** package for more details on these classes.
-
-## Simple Reactor Examples
-
-### The Broker
-
-The reactor examples come with a sample broker which can be used by other examples and which also works as an example itself. For now we'll just start up the broker example and tell it to listen on port 8888:
-
-````
-$ ruby ../examples/ruby/reactor/broker.rb  --address=0.0.0.0:8888
-Listening on 0.0.0.0:8888
-````
-
-This example broker will receive messages, create queues as needed, and deliver messages to endpoints.
-
-### Hello World Using A Broker
-
-Our first example creates an endpoint that sends messages to a queue to which it is subscribed. So it both sends and receives its message in one pass.
-
-To start it, simply run:
-
-```
-$ ruby ../examples/ruby/reactor/helloworld.rb --address=0.0.0.0:8888 --queue=examples
-Hello world!
-```
-
-As you can see, the classic message was output by the example. Now let's take a look at what's going on under the covers.
-
-#### Events When Talking To A Broker
-
-The following events occur while **helloworld.rb** runs:
-
- * **on_start** - Fired when the application is started.
- * **on_sendable** - Fired when a message can be sent.
- * **on_message** - Fired when a message is received.
-
-### Hello World Without A Broker required
-
-The next example we'll look at will send the classic "Hello world" message to itself directly. This example shows some very fundamental elements of the reactor APIs that you should understand.
-
-To launch the example:
-
-```
- $ ruby helloworld_direct.rb --address=0.0.0.0:8888/examples
- Hello world!
-```
-
-Not very different from the example that uses the broker, which is what we'd expect from the outside. But let's take a look inside of the example and see how it's different at that level
-
-The direct version takes on the responsibility for listening to incoming connections as well as making an outgoing connection. So we see the following additional events occurring:
-
- * **on_accepted** - Fired when a message is received.
- * **on_connection_closed** - Fired when an endpoint closes its connection.
-
-## More Complex Reactor Examples
-
-Now that we've covered the basics with the archetypical hello world app, let's look at some more interesting examples.
-
-There are four example applications that demonstrate how to send and receive messages both directly and through an intermediary, such as a broker:
-
- * **simple_send.rb** - sends messages to a receiver at a specific address and receives responses via an intermediary,
- * **simple_recv.rb** - receives messages from via an intermediary,
- * **direct_send.rb** - sends messages directly to a receiver and listens for responses itself, and
- * **direct_recv.rb** - receives messages directly.
-
- Simple send and direct send may, at first, seem to be so similar that you wonder why they're not just the same applciation. And I know for me I was wonder as I wrote the list above why there were two examples. The reason is that **simple_send.rb** uses the intermediary transfer responses to the messages it sends, while **direct_send.rb** uses an *Acceptor* to listen for an process responses.
-
- You can use the examples in the follow ways:
-
- ```
- simple_send.rb -> broker <- simple_recv.rb
- simple_send.rb -> direct_recv.rb
- direct_send.rb -> simple_recv.rb
- ```
-
-In this set of examples we see the following event occurring, in addition to what we've seen before:
-
- * **on_disconnected** - Fired when the transport is closed.
-
-## Now About That Broker example
-
-The **broker.rb** example application is a nice demonstration of doing something more interesting in Ruby with Proton.
-
-The way the broker works is to listen to incoming connections, examine the components of the address for that connection, attach that connection to an exchange managing that address and then it sends any messages destined for that address to them.
-
-The components of the broker example include:
- * **Broker** - A class that extends the MessagingHandler class. It accepts incoming connections, manages subscribing them to exchanges, and transfers messages between them.
- * **Exchange** - A class that represents a message queue, tracking what endpoints are subscribed to it.
-
-The Broker manages a map connecting a queue address to the instance of Exchange that holds references to the endpoints of interest.
-
-The broker application demonstrates a new set of reactor events:
-
- * **on_link_opening** - Fired when a remote link is opened but the local end is not yet open. From this event the broker grabs the address and subscribes the link to an exchange for that address.
- * **on_link_closing** - Fired when a remote link is closed but the local end is still open. From this event the broker grabs the address and unsubscribes the link from that exchange.
- * **on_connection_closing** - Fired when a remote connection is closed but the local end is still open.
- * **on_disconnected** - Fired when the protocol transport has closed. The broker removes all links for the disconnected connection, avoiding workign with endpoints that are now gone.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/broker.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/broker.rb b/examples/ruby/reactor/broker.rb
deleted file mode 100644
index 7882d9a..0000000
--- a/examples/ruby/reactor/broker.rb
+++ /dev/null
@@ -1,200 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-require 'pathname'
-
-require_relative '../lib/debugging'
-
-class Exchange
-
-  include Debugging
-
-  def initialize(dynamic = false)
-    @dynamic = dynamic
-    @queue = Queue.new
-    @consumers = []
-  end
-
-  def subscribe(consumer)
-    debug("subscribing #{consumer}") if $options[:debug]
-    @consumers << (consumer)
-    debug(" there are #{@consumers.size} consumers") if $options[:debug]
-  end
-
-  def unsubscribe(consumer)
-    debug("unsubscribing #{consumer}") if $options[:debug]
-    if @consumers.include?(consumer)
-      @consumers.delete(consumer)
-    else
-      debug(" consumer doesn't exist") if $options[:debug]
-    end
-    debug("  there are #{@consumers.size} consumers") if $options[:debug]
-    @consumers.empty? && (@dynamic || @queue.empty?)
-  end
-
-  def publish(message)
-    debug("queueing message: #{message.body}") if $options[:debug]
-    @queue << message
-    self.dispatch
-  end
-
-  def dispatch(consumer = nil)
-    debug("dispatching: consumer=#{consumer}") if $options[:debug]
-    if consumer
-      c = [consumer]
-    else
-      c = @consumers
-    end
-
-    while self.deliver_to(c) do
-    end
-  end
-
-  def deliver_to(consumers)
-    debug("delivering to #{consumers.size} consumer(s)") if $options[:debug]
-    result = false
-    consumers.each do |consumer|
-      debug(" current consumer=#{consumer} credit=#{consumer.credit}") if $options[:debug]
-      if consumer.credit > 0 && !@queue.empty?
-        consumer.send(@queue.pop(true))
-        result = true
-      end
-    end
-    return result
-  end
-
-end
-
-class Broker < Qpid::Proton::Handler::MessagingHandler
-
-  include Debugging
-
-  def initialize(url)
-    super()
-    @url = url
-    @queues = {}
-  end
-
-  def on_start(event)
-    debug("on_start event") if $options[:debug]
-    @acceptor = event.container.listen(@url)
-    print "Listening on #{@url}\n"
-  end
-
-  def queue(address)
-    debug("fetching queue for #{address}: (there are #{@queues.size} queues)") if $options[:debug]
-    unless @queues.has_key?(address)
-      debug(" creating new queue") if $options[:debug]
-      @queues[address] = Exchange.new
-    else
-      debug(" using existing queue") if $options[:debug]
-    end
-    result = @queues[address]
-    debug(" returning #{result}") if $options[:debug]
-    return result
-  end
-
-  def on_link_opening(event)
-    debug("processing on_link_opening") if $options[:debug]
-    debug("link is#{event.link.sender? ? '' : ' not'} a sender") if $options[:debug]
-    if event.link.sender?
-      if event.link.remote_source.dynamic?
-        address = SecureRandom.uuid
-        event.link.source.address = address
-        q = Exchange.new(true)
-        @queues[address] = q
-        q.subscribe(event.link)
-      elsif event.link.remote_source.address
-        event.link.source.address = event.link.remote_source.address
-        self.queue(event.link.source.address).subscribe(event.link)
-      end
-    elsif event.link.remote_target.address
-      event.link.target.address = event.link.remote_target.address
-    end
-  end
-
-  def unsubscribe(link)
-    debug("unsubscribing #{link.address}") if $options[:debug]
-    if @queues.has_key?(link.source.address)
-      if @queues[link.source.address].unsubscribe(link)
-        @queues.delete(link.source.address)
-      end
-    end
-  end
-
-  def on_link_closing(event)
-    self.unsubscribe(event.link) if event.link.sender?
-  end
-
-  def on_connection_closing(event)
-    self.remove_stale_consumers(event.connection)
-  end
-
-  def on_disconnected(event)
-    self.remove_stale_consumers(event.connection)
-  end
-
-  def remove_stale_consumers(connection)
-    l = connection.link_head(Qpid::Proton::Endpoint::REMOTE_ACTIVE)
-    while !l.nil?
-      self.unsubscribe(l) if l.sender?
-      l = l.next(Qpid::Proton::Endpoint::REMOTE_ACTIVE)
-    end
-  end
-
-  def on_sendable(event)
-    debug("on_sendable event") if $options[:debug]
-    q = self.queue(event.link.source.address)
-    debug(" dispatching #{event.message} to #{q}") if $options[:debug]
-    q.dispatch(event.link)
-  end
-
-  def on_message(event)
-    debug("on_message event") if $options[:debug]
-    q = self.queue(event.link.target.address)
-    debug(" dispatching #{event.message} to #{q}") if $options[:debug]
-    q.publish(event.message)
-  end
-
-end
-
-$options = {
-  :address => "localhost:5672",
-  :debug => false
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: #{Pathname.new(__FILE__).basename} [$options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{$options[:address]}).") do |address|
-    $options[:address] = address
-  end
-
-  opts.on("-d", "--debug", "Enable debugging output (def. #{$options[:debug]})") do
-    $options[:debug] = true
-  end
-
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(Broker.new($options[:address])).run
-rescue Interrupt
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/client.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/client.rb b/examples/ruby/reactor/client.rb
deleted file mode 100644
index 8c38f38..0000000
--- a/examples/ruby/reactor/client.rb
+++ /dev/null
@@ -1,82 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-class Client < Qpid::Proton::Handler::MessagingHandler
-
-  def initialize(url, requests)
-    super()
-    @url = url
-    @requests = requests
-  end
-
-  def on_start(event)
-    @sender = event.container.create_sender(@url)
-    @receiver = event.container.create_receiver(@sender.connection, :dynamic => true)
-  end
-
-  def next_request
-    if @receiver.remote_source.address
-      req = Qpid::Proton::Message.new
-      req.reply_to = @receiver.remote_source.address
-      req.body = @requests.first
-      puts "-> #{req.body}"
-      @sender.send(req)
-    end
-  end
-
-  def on_link_opened(event)
-    if event.receiver == @receiver
-      next_request
-    end
-  end
-
-  def on_message(event)
-    puts "<- #{event.message.body}"
-    @requests.delete_at(0)
-    if !@requests.empty?
-      next_request
-    else
-      event.connection.close
-    end
-  end
-
-  def on_transport_error(event)
-    raise "Connection error: #{event.transport.condition}"
-  end
-
-end
-
-REQUESTS = ["Twas brillig, and the slithy toves",
-            "Did gire and gymble in the wabe.",
-            "All mimsy were the borogroves,",
-            "And the mome raths outgrabe."]
-
-options = {
-  :address => "localhost:5672/examples",
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: client.rb [options]"
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(Client.new(options[:address], REQUESTS)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/direct_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/direct_recv.rb b/examples/ruby/reactor/direct_recv.rb
deleted file mode 100644
index 2e19b04..0000000
--- a/examples/ruby/reactor/direct_recv.rb
+++ /dev/null
@@ -1,60 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-require_relative '../lib/send_and_receive'
-
-class DirectReceive < ExampleReceive
-
-  def initialize(url, expected)
-    super
-  end
-
-  def on_start(event)
-    @acceptor = event.container.listen(self.url)
-  end
-
-  def on_message(event)
-    super(event)
-    @acceptor.close if self.finished?
-  end
-
-end
-
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(DirectReceive.new(options[:address], options[:messages])).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/direct_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/direct_send.rb b/examples/ruby/reactor/direct_send.rb
deleted file mode 100644
index 22ce7de..0000000
--- a/examples/ruby/reactor/direct_send.rb
+++ /dev/null
@@ -1,59 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-require_relative '../lib/send_and_receive'
-
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
-class SimpleSend < ExampleSend
-
-  def initialize(url, messages)
-    super(url, messages)
-  end
-
-  def on_start(event)
-    @acceptor = event.container.listen(url)
-  end
-
-end
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run
-rescue Interrupt => error
-  puts "ERROR: #{error}"
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/helloworld.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/helloworld.rb b/examples/ruby/reactor/helloworld.rb
deleted file mode 100644
index 9b02e8a..0000000
--- a/examples/ruby/reactor/helloworld.rb
+++ /dev/null
@@ -1,73 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-class HelloWorld < Qpid::Proton::Handler::MessagingHandler
-
-  def initialize(server, address)
-    super()
-    @server = server
-    @address = address
-  end
-
-  def on_start(event)
-    conn = event.container.connect(:address => @server)
-    event.container.create_sender(conn, :target => @address)
-    event.container.create_receiver(conn, :source => @address)
-  end
-
-  def on_sendable(event)
-    msg = Qpid::Proton::Message.new
-    msg.body = "Hello world!"
-    event.sender.send(msg)
-    event.sender.close
-  end
-
-  def on_message(event)
-    puts event.message.body
-    event.connection.close
-  end
-
-  def on_transport_error(event)
-    raise "Connection error: #{event.transport.condition}"
-  end
-end
-
-options = {
-  :address => "localhost:5672",
-  :queue => "examples"
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: helloworld_direct.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-q", "--queue=QUEUE", "Send messages to QUEUE (def. #{options[:queue]})") do |queue|
-    options[:queue] = queue
-  end
-
-end.parse!
-
-hw = HelloWorld.new(options[:address], "examples")
-Qpid::Proton::Reactor::Container.new(hw).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/helloworld_direct.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/helloworld_direct.rb b/examples/ruby/reactor/helloworld_direct.rb
deleted file mode 100644
index e98cc1f..0000000
--- a/examples/ruby/reactor/helloworld_direct.rb
+++ /dev/null
@@ -1,74 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-options = {
-  :address => "localhost:5672/examples",
-}
-
-class HelloWorldDirect < Qpid::Proton::Handler::MessagingHandler
-
-  include Qpid::Proton::Util::Wrapper
-
-  def initialize(url)
-    super()
-    @url = url
-  end
-
-  def on_start(event)
-    @acceptor = event.container.listen(@url)
-    event.container.create_sender(@url)
-  end
-
-  def on_sendable(event)
-    msg = Qpid::Proton::Message.new
-    msg.body = "Hello world!"
-    event.sender.send(msg)
-    event.sender.close
-  end
-
-  def on_message(event)
-    puts "#{event.message.body}"
-  end
-
-  def on_accepted(event)
-    event.connection.close
-  end
-
-  def on_connection_closed(event)
-    @acceptor.close
-  end
-
-end
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: helloworld_direct.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(HelloWorldDirect.new(options[:address])).run
-rescue Interrupt => error
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/server.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/server.rb b/examples/ruby/reactor/server.rb
deleted file mode 100644
index 9373272..0000000
--- a/examples/ruby/reactor/server.rb
+++ /dev/null
@@ -1,76 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-class Server < Qpid::Proton::Handler::MessagingHandler
-
-  def initialize(url)
-    super()
-    @url = Qpid::Proton::URL.new url
-    @address = @url.path
-    @senders = {}
-  end
-
-  def on_start(event)
-    @container = event.container
-    @conn = @container.connect(:url => @url)
-    @receiver = @container.create_receiver(@conn, :source => @address)
-    @relay = nil
-  end
-
-  def on_connection_opened(event)
-    if event.connection.remote_offered_capabilities &&
-      event.connection.remote_offered_capabilities.contain?("ANONYMOUS-RELAY")
-      @relay = @container.create_sender(@conn, nil)
-    end
-  end
-
-  def on_message(event)
-    msg = event.message
-    puts "<- #{msg.body}"
-    sender = @relay || @senders[msg.reply_to]
-    if sender.nil?
-      sender = @container.create_sender(@conn, :target => msg.reply_to)
-      @senders[msg.reply_to] = sender
-    end
-    reply = Qpid::Proton::Message.new
-    reply.address = msg.reply_to
-    reply.body = msg.body.upcase
-    puts "-> #{reply.body}"
-    reply.correlation_id = msg.correlation_id
-    sender.send(reply)
-  end
-
-  def on_transport_error(event)
-    raise "Connection error: #{event.transport.condition}"
-  end
-end
-
-options = {
-  :address => "localhost:5672/examples",
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: server.rb [options]"
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(Server.new(options[:address])).run()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/simple_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/simple_recv.rb b/examples/ruby/reactor/simple_recv.rb
deleted file mode 100644
index 55a37ee..0000000
--- a/examples/ruby/reactor/simple_recv.rb
+++ /dev/null
@@ -1,58 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-require_relative '../lib/send_and_receive'
-
-class Receiver < ExampleReceive
-
-  def initialize(url, count)
-    super(url, count)
-  end
-
-  def on_start(event)
-    event.container.create_receiver(@url)
-  end
-
-end
-
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(Receiver.new(options[:address], options[:messages])).run
-rescue Interrupt
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/reactor/simple_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/reactor/simple_send.rb b/examples/ruby/reactor/simple_send.rb
deleted file mode 100644
index 1dd4150..0000000
--- a/examples/ruby/reactor/simple_send.rb
+++ /dev/null
@@ -1,55 +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.
-#++
-
-require 'qpid_proton'
-require 'optparse'
-
-require_relative '../lib/send_and_receive'
-
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
-class SimpleSend < ExampleSend
-
-  def initialize(url, messages)
-    super(url, messages)
-  end
-
-  def on_start(event)
-    event.container.create_sender(url)
-  end
-
-end
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/registry_test.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/registry_test.rb b/examples/ruby/registry_test.rb
deleted file mode 100644
index b4b1f6c..0000000
--- a/examples/ruby/registry_test.rb
+++ /dev/null
@@ -1,76 +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.
-#++
-
-require 'qpid_proton'
-require 'weakref'
-
-def show_registry(registry)
-  registry.each_pair do |key, value|
-    registry.delete(key) if value.weakref_alive?
-  end
-  puts "The contents of the registry: size=#{registry.size}"
-end
-
-def show_object_count(clazz)
-  puts "There are #{ObjectSpace.each_object(clazz).count} instances of #{clazz}."
-end
-
-impl = Cproton.pn_transport
-implclazz = impl.class
-transport = Qpid::Proton::Transport.wrap(impl)
-
-puts "Initial setup:"
-show_object_count(Qpid::Proton::Transport)
-show_object_count(implclazz)
-
-transport = nil
-
-show_registry(Qpid::Proton.registry)
-
-ObjectSpace.garbage_collect
-
-puts "After garbage collection:"
-show_object_count(Qpid::Proton::Transport)
-show_object_count(implclazz)
-
-MAXCOUNT=100000
-(1..MAXCOUNT).each do |which|
-  nimpl = Cproton.pn_transport
-  Cproton.pn_incref(nimpl)
-  transport = Qpid::Proton::Transport.wrap(nimpl)
-  transport = Qpid::Proton::Transport.wrap(nimpl)
-end
-
-transport = nil
-
-puts "After creating #{MAXCOUNT} instances"
-show_object_count(Qpid::Proton::Transport)
-show_object_count(implclazz)
-show_registry(Qpid::Proton.registry)
-
-ObjectSpace.garbage_collect
-
-transport = Qpid::Proton::Transport.wrap(impl)
-
-puts "After garbage collection:"
-puts "impl=#{impl}"
-puts "transport=#{transport}"
-show_object_count(Qpid::Proton::Transport)
-show_object_count(implclazz)
-show_registry(Qpid::Proton.registry)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/server.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/server.rb b/examples/ruby/server.rb
new file mode 100644
index 0000000..9373272
--- /dev/null
+++ b/examples/ruby/server.rb
@@ -0,0 +1,76 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class Server < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url)
+    super()
+    @url = Qpid::Proton::URL.new url
+    @address = @url.path
+    @senders = {}
+  end
+
+  def on_start(event)
+    @container = event.container
+    @conn = @container.connect(:url => @url)
+    @receiver = @container.create_receiver(@conn, :source => @address)
+    @relay = nil
+  end
+
+  def on_connection_opened(event)
+    if event.connection.remote_offered_capabilities &&
+      event.connection.remote_offered_capabilities.contain?("ANONYMOUS-RELAY")
+      @relay = @container.create_sender(@conn, nil)
+    end
+  end
+
+  def on_message(event)
+    msg = event.message
+    puts "<- #{msg.body}"
+    sender = @relay || @senders[msg.reply_to]
+    if sender.nil?
+      sender = @container.create_sender(@conn, :target => msg.reply_to)
+      @senders[msg.reply_to] = sender
+    end
+    reply = Qpid::Proton::Message.new
+    reply.address = msg.reply_to
+    reply.body = msg.body.upcase
+    puts "-> #{reply.body}"
+    reply.correlation_id = msg.correlation_id
+    sender.send(reply)
+  end
+
+  def on_transport_error(event)
+    raise "Connection error: #{event.transport.condition}"
+  end
+end
+
+options = {
+  :address => "localhost:5672/examples",
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: server.rb [options]"
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(Server.new(options[:address])).run()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/3d258158/examples/ruby/simple_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/simple_recv.rb b/examples/ruby/simple_recv.rb
new file mode 100644
index 0000000..47b21ed
--- /dev/null
+++ b/examples/ruby/simple_recv.rb
@@ -0,0 +1,69 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class Receiver < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url, count)
+    super()
+    @url = url
+    @expected = count
+    @received = 0
+  end
+
+  def on_start(event)
+    event.container.create_receiver(@url)
+  end
+
+  def on_message(event)
+    if @expected.zero? || (@received < @expected)
+      puts "Received: #{event.message.body}"
+      @received = @received + 1
+      if @received == @expected
+        event.connection.close
+      end
+    end
+  end
+
+end
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(Receiver.new(options[:address], options[:messages])).run
+rescue Interrupt
+end


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


[10/10] qpid-proton git commit: PROTON-1064: [ruby] use old examples as compatibility auto-test

Posted by ac...@apache.org.
PROTON-1064: [ruby] use old examples as compatibility auto-test

Added the old reactor examples to the auto-test suite as a basic check
for backwards-compatibility breakage.


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

Branch: refs/heads/master
Commit: 86de6b5896c56b3a26419f839b061a30da3f7f89
Parents: 8155c5a
Author: Alan Conway <ac...@redhat.com>
Authored: Mon Nov 6 16:33:23 2017 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/ruby/CMakeLists.txt           |   2 +
 .../bindings/ruby/tests/old_examples/README.md  |   8 +
 .../bindings/ruby/tests/old_examples/broker.rb  | 200 +++++++++++++++++++
 .../bindings/ruby/tests/old_examples/client.rb  |  82 ++++++++
 .../ruby/tests/old_examples/direct_recv.rb      |  60 ++++++
 .../ruby/tests/old_examples/direct_send.rb      |  59 ++++++
 .../ruby/tests/old_examples/helloworld.rb       |  73 +++++++
 .../tests/old_examples/helloworld_direct.rb     |  74 +++++++
 .../ruby/tests/old_examples/lib/debugging.rb    |  26 +++
 .../ruby/tests/old_examples/lib/driver.rb       |  69 +++++++
 .../tests/old_examples/lib/qpid_examples.rb     |  27 +++
 .../ruby/tests/old_examples/lib/selectable.rb   | 120 +++++++++++
 .../tests/old_examples/lib/send_and_receive.rb  |  90 +++++++++
 .../ruby/tests/old_examples/old_example_test.rb |  85 ++++++++
 .../bindings/ruby/tests/old_examples/server.rb  |  76 +++++++
 .../ruby/tests/old_examples/simple_recv.rb      |  58 ++++++
 .../ruby/tests/old_examples/simple_send.rb      |  55 +++++
 17 files changed, 1164 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/CMakeLists.txt b/proton-c/bindings/ruby/CMakeLists.txt
index 3397b43..46b7ba8 100644
--- a/proton-c/bindings/ruby/CMakeLists.txt
+++ b/proton-c/bindings/ruby/CMakeLists.txt
@@ -113,6 +113,8 @@ if (result EQUAL 0)  # Have minitest
     set_tests_properties(${name} PROPERTIES ENVIRONMENT "PATH=${PATH};RUBYLIB=${RUBYLIB}")
   endmacro()
   add_ruby_test(example_test.rb WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/examples/ruby)
+  # Old examples for backwards compatibility testing.
+  add_ruby_test(old_example_test.rb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/old_examples)
 
   file(GLOB TESTS tests/test_*.rb)
   file(GLOB SPECS spec/*_spec.rb)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/README.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/README.md b/proton-c/bindings/ruby/tests/old_examples/README.md
new file mode 100644
index 0000000..1516e86
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/README.md
@@ -0,0 +1,8 @@
+# OLD EXAMPLES FOR BACKWARDS COMPATIBILITY TESTING
+
+These examples are from 0.18.1, before the ruby IO refactoring.
+
+They are run as part of the regression test suite to catch breaks in backwards
+compatibility.
+
+They will be removed when all deprecated features have been removed.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/broker.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/broker.rb b/proton-c/bindings/ruby/tests/old_examples/broker.rb
new file mode 100644
index 0000000..e1ababd
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/broker.rb
@@ -0,0 +1,200 @@
+#--
+# 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.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+require 'pathname'
+
+require_relative 'lib/debugging'
+
+class Exchange
+
+  include Debugging
+
+  def initialize(dynamic = false)
+    @dynamic = dynamic
+    @queue = Queue.new
+    @consumers = []
+  end
+
+  def subscribe(consumer)
+    debug("subscribing #{consumer}") if $options[:debug]
+    @consumers << (consumer)
+    debug(" there are #{@consumers.size} consumers") if $options[:debug]
+  end
+
+  def unsubscribe(consumer)
+    debug("unsubscribing #{consumer}") if $options[:debug]
+    if @consumers.include?(consumer)
+      @consumers.delete(consumer)
+    else
+      debug(" consumer doesn't exist") if $options[:debug]
+    end
+    debug("  there are #{@consumers.size} consumers") if $options[:debug]
+    @consumers.empty? && (@dynamic || @queue.empty?)
+  end
+
+  def publish(message)
+    debug("queueing message: #{message.body}") if $options[:debug]
+    @queue << message
+    self.dispatch
+  end
+
+  def dispatch(consumer = nil)
+    debug("dispatching: consumer=#{consumer}") if $options[:debug]
+    if consumer
+      c = [consumer]
+    else
+      c = @consumers
+    end
+
+    while self.deliver_to(c) do
+    end
+  end
+
+  def deliver_to(consumers)
+    debug("delivering to #{consumers.size} consumer(s)") if $options[:debug]
+    result = false
+    consumers.each do |consumer|
+      debug(" current consumer=#{consumer} credit=#{consumer.credit}") if $options[:debug]
+      if consumer.credit > 0 && !@queue.empty?
+        consumer.send(@queue.pop(true))
+        result = true
+      end
+    end
+    return result
+  end
+
+end
+
+class Broker < Qpid::Proton::Handler::MessagingHandler
+
+  include Debugging
+
+  def initialize(url)
+    super()
+    @url = url
+    @queues = {}
+  end
+
+  def on_start(event)
+    debug("on_start event") if $options[:debug]
+    @acceptor = event.container.listen(@url)
+    print "Listening on #{@url}\n"
+  end
+
+  def queue(address)
+    debug("fetching queue for #{address}: (there are #{@queues.size} queues)") if $options[:debug]
+    unless @queues.has_key?(address)
+      debug(" creating new queue") if $options[:debug]
+      @queues[address] = Exchange.new
+    else
+      debug(" using existing queue") if $options[:debug]
+    end
+    result = @queues[address]
+    debug(" returning #{result}") if $options[:debug]
+    return result
+  end
+
+  def on_link_opening(event)
+    debug("processing on_link_opening") if $options[:debug]
+    debug("link is#{event.link.sender? ? '' : ' not'} a sender") if $options[:debug]
+    if event.link.sender?
+      if event.link.remote_source.dynamic?
+        address = SecureRandom.uuid
+        event.link.source.address = address
+        q = Exchange.new(true)
+        @queues[address] = q
+        q.subscribe(event.link)
+      elsif event.link.remote_source.address
+        event.link.source.address = event.link.remote_source.address
+        self.queue(event.link.source.address).subscribe(event.link)
+      end
+    elsif event.link.remote_target.address
+      event.link.target.address = event.link.remote_target.address
+    end
+  end
+
+  def unsubscribe(link)
+    debug("unsubscribing #{link.address}") if $options[:debug]
+    if @queues.has_key?(link.source.address)
+      if @queues[link.source.address].unsubscribe(link)
+        @queues.delete(link.source.address)
+      end
+    end
+  end
+
+  def on_link_closing(event)
+    self.unsubscribe(event.link) if event.link.sender?
+  end
+
+  def on_connection_closing(event)
+    self.remove_stale_consumers(event.connection)
+  end
+
+  def on_disconnected(event)
+    self.remove_stale_consumers(event.connection)
+  end
+
+  def remove_stale_consumers(connection)
+    l = connection.link_head(Qpid::Proton::Endpoint::REMOTE_ACTIVE)
+    while !l.nil?
+      self.unsubscribe(l) if l.sender?
+      l = l.next(Qpid::Proton::Endpoint::REMOTE_ACTIVE)
+    end
+  end
+
+  def on_sendable(event)
+    debug("on_sendable event") if $options[:debug]
+    q = self.queue(event.link.source.address)
+    debug(" dispatching #{event.message} to #{q}") if $options[:debug]
+    q.dispatch(event.link)
+  end
+
+  def on_message(event)
+    debug("on_message event") if $options[:debug]
+    q = self.queue(event.link.target.address)
+    debug(" dispatching #{event.message} to #{q}") if $options[:debug]
+    q.publish(event.message)
+  end
+
+end
+
+$options = {
+  :address => "localhost:5672",
+  :debug => false
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: #{Pathname.new(__FILE__).basename} [$options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{$options[:address]}).") do |address|
+    $options[:address] = address
+  end
+
+  opts.on("-d", "--debug", "Enable debugging output (def. #{$options[:debug]})") do
+    $options[:debug] = true
+  end
+
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(Broker.new($options[:address])).run
+rescue Interrupt
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/client.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/client.rb b/proton-c/bindings/ruby/tests/old_examples/client.rb
new file mode 100644
index 0000000..8c38f38
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/client.rb
@@ -0,0 +1,82 @@
+#--
+# 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.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class Client < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url, requests)
+    super()
+    @url = url
+    @requests = requests
+  end
+
+  def on_start(event)
+    @sender = event.container.create_sender(@url)
+    @receiver = event.container.create_receiver(@sender.connection, :dynamic => true)
+  end
+
+  def next_request
+    if @receiver.remote_source.address
+      req = Qpid::Proton::Message.new
+      req.reply_to = @receiver.remote_source.address
+      req.body = @requests.first
+      puts "-> #{req.body}"
+      @sender.send(req)
+    end
+  end
+
+  def on_link_opened(event)
+    if event.receiver == @receiver
+      next_request
+    end
+  end
+
+  def on_message(event)
+    puts "<- #{event.message.body}"
+    @requests.delete_at(0)
+    if !@requests.empty?
+      next_request
+    else
+      event.connection.close
+    end
+  end
+
+  def on_transport_error(event)
+    raise "Connection error: #{event.transport.condition}"
+  end
+
+end
+
+REQUESTS = ["Twas brillig, and the slithy toves",
+            "Did gire and gymble in the wabe.",
+            "All mimsy were the borogroves,",
+            "And the mome raths outgrabe."]
+
+options = {
+  :address => "localhost:5672/examples",
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: client.rb [options]"
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(Client.new(options[:address], REQUESTS)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/direct_recv.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/direct_recv.rb b/proton-c/bindings/ruby/tests/old_examples/direct_recv.rb
new file mode 100644
index 0000000..e8b52f3
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/direct_recv.rb
@@ -0,0 +1,60 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+require_relative 'lib/send_and_receive'
+
+class DirectReceive < ExampleReceive
+
+  def initialize(url, expected)
+    super
+  end
+
+  def on_start(event)
+    @acceptor = event.container.listen(self.url)
+  end
+
+  def on_message(event)
+    super(event)
+    @acceptor.close if self.finished?
+  end
+
+end
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(DirectReceive.new(options[:address], options[:messages])).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/direct_send.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/direct_send.rb b/proton-c/bindings/ruby/tests/old_examples/direct_send.rb
new file mode 100644
index 0000000..2164304
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/direct_send.rb
@@ -0,0 +1,59 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+require_relative 'lib/send_and_receive'
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+class SimpleSend < ExampleSend
+
+  def initialize(url, messages)
+    super(url, messages)
+  end
+
+  def on_start(event)
+    @acceptor = event.container.listen(url)
+  end
+
+end
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run
+rescue Interrupt => error
+  puts "ERROR: #{error}"
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/helloworld.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/helloworld.rb b/proton-c/bindings/ruby/tests/old_examples/helloworld.rb
new file mode 100644
index 0000000..9b02e8a
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/helloworld.rb
@@ -0,0 +1,73 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class HelloWorld < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(server, address)
+    super()
+    @server = server
+    @address = address
+  end
+
+  def on_start(event)
+    conn = event.container.connect(:address => @server)
+    event.container.create_sender(conn, :target => @address)
+    event.container.create_receiver(conn, :source => @address)
+  end
+
+  def on_sendable(event)
+    msg = Qpid::Proton::Message.new
+    msg.body = "Hello world!"
+    event.sender.send(msg)
+    event.sender.close
+  end
+
+  def on_message(event)
+    puts event.message.body
+    event.connection.close
+  end
+
+  def on_transport_error(event)
+    raise "Connection error: #{event.transport.condition}"
+  end
+end
+
+options = {
+  :address => "localhost:5672",
+  :queue => "examples"
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: helloworld_direct.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-q", "--queue=QUEUE", "Send messages to QUEUE (def. #{options[:queue]})") do |queue|
+    options[:queue] = queue
+  end
+
+end.parse!
+
+hw = HelloWorld.new(options[:address], "examples")
+Qpid::Proton::Reactor::Container.new(hw).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/helloworld_direct.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/helloworld_direct.rb b/proton-c/bindings/ruby/tests/old_examples/helloworld_direct.rb
new file mode 100644
index 0000000..e98cc1f
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/helloworld_direct.rb
@@ -0,0 +1,74 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+options = {
+  :address => "localhost:5672/examples",
+}
+
+class HelloWorldDirect < Qpid::Proton::Handler::MessagingHandler
+
+  include Qpid::Proton::Util::Wrapper
+
+  def initialize(url)
+    super()
+    @url = url
+  end
+
+  def on_start(event)
+    @acceptor = event.container.listen(@url)
+    event.container.create_sender(@url)
+  end
+
+  def on_sendable(event)
+    msg = Qpid::Proton::Message.new
+    msg.body = "Hello world!"
+    event.sender.send(msg)
+    event.sender.close
+  end
+
+  def on_message(event)
+    puts "#{event.message.body}"
+  end
+
+  def on_accepted(event)
+    event.connection.close
+  end
+
+  def on_connection_closed(event)
+    @acceptor.close
+  end
+
+end
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: helloworld_direct.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(HelloWorldDirect.new(options[:address])).run
+rescue Interrupt => error
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/lib/debugging.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/lib/debugging.rb b/proton-c/bindings/ruby/tests/old_examples/lib/debugging.rb
new file mode 100644
index 0000000..5065d51
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/lib/debugging.rb
@@ -0,0 +1,26 @@
+#--
+# 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.
+#++
+
+module Debugging
+
+  def debug(text)
+    print "[#{Time.now.strftime('%s')}] #{text}\n"
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/lib/driver.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/lib/driver.rb b/proton-c/bindings/ruby/tests/old_examples/lib/driver.rb
new file mode 100644
index 0000000..4e223d0
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/lib/driver.rb
@@ -0,0 +1,69 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+class Driver
+
+  def initialize
+    @selectables = {}
+  end
+
+  def add(selectable)
+    @selectables[selectable.fileno] = selectable
+  end
+
+  def process
+    reading = []
+    writing = []
+
+    @selectables.each_value do |sel|
+      if sel.closed? || sel.fileno.nil?
+        @selectables.delete(sel.fileno)
+      else
+        begin
+          reading << sel.to_io if sel.reading?
+          writing << sel.to_io if sel.writing?
+        rescue Exception => error
+          puts "Error: #{error}"
+          puts error.backtrace.join("\n");
+          # @selectables.delete(sel.fileno)
+        end
+      end
+    end
+
+    read_from, write_to = IO.select(reading, writing, [], 0)
+
+    unless read_from.nil?
+      read_from.each do |r|
+        sel = @selectables[r.fileno]
+        sel.readable unless sel.nil? || sel.closed?
+      end
+    end
+
+    begin
+      unless write_to.nil?
+        write_to.each do |w|
+          sel = @selectables[w.fileno]
+          sel.writable unless sel.nil? || sel.closed?
+        end
+      end
+
+    end
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/lib/qpid_examples.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/lib/qpid_examples.rb b/proton-c/bindings/ruby/tests/old_examples/lib/qpid_examples.rb
new file mode 100644
index 0000000..665812d
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/lib/qpid_examples.rb
@@ -0,0 +1,27 @@
+#--
+# 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.
+#++
+
+require "qpid_proton"
+
+require "selectable"
+require "driver"
+require "socket"
+require "monitor"
+
+include Socket::Constants

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/lib/selectable.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/lib/selectable.rb b/proton-c/bindings/ruby/tests/old_examples/lib/selectable.rb
new file mode 100644
index 0000000..779ea24
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/lib/selectable.rb
@@ -0,0 +1,120 @@
+#--
+# 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.
+#++
+
+class Selectable
+
+  attr_reader :transport
+
+  def initialize(transport, socket)
+    @transport = transport
+    @socket = socket
+    @socket.autoclose = true
+    @write_done = false
+    @read_done = false
+  end
+
+  def closed?
+    return true if @socket.closed?
+    return false if !@read_done && !@write_done
+    @socket.close
+    true
+  end
+
+  def fileno
+    @socket.fileno unless @socket.closed?
+  end
+
+  def to_io
+    @socket
+  end
+
+  def reading?
+    return false if @read_done
+    c = @transport.capacity
+    if c > 0
+      return true
+    elsif c < 0
+      @read_done = true
+      return false
+    else
+      return false
+    end
+  end
+
+  def writing?
+    return false if @write_done
+    begin
+      p = @transport.pending
+      if p > 0
+        return true
+      elsif p < 0
+        @write_done = true
+        return false
+      else
+        return false
+      end
+    rescue Qpid::Proton::TransportError => error
+      @write_done = true
+      return false
+    end
+  end
+
+  def readable
+    c = @transport.capacity
+    if c > 0
+      begin
+        data = @socket.recv(c)
+        if data
+          @transport.push(data)
+        else
+          @transport.close_tail
+        end
+      rescue Exception => error
+        puts "read error; #{error}"
+        @transport.close_tail
+        @read_done = true
+      end
+    elsif c < 0
+      @read_done = true
+    end
+  end
+
+  def writable
+    begin
+      p = @transport.pending
+      if p > 0
+        data = @transport.peek(p)
+        n = @socket.send(data, 0)
+        @transport.pop(n)
+      elsif p < 0
+        @write_done = true
+      end
+    rescue Exception => error
+      puts "write error: #{error}"
+      puts error.backtrace.join("\n")
+      @transport.close_head
+      @write_done = true
+    end
+  end
+
+  def tick(now)
+    @transport.tick(now)
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/lib/send_and_receive.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/lib/send_and_receive.rb b/proton-c/bindings/ruby/tests/old_examples/lib/send_and_receive.rb
new file mode 100644
index 0000000..9fd7417
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/lib/send_and_receive.rb
@@ -0,0 +1,90 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+class ExampleSend < Qpid::Proton::Handler::MessagingHandler
+
+  attr_reader :url
+
+  def initialize(url, expected)
+    super()
+    @url = url
+    @sent = 0
+    @confirmed = 0
+    @expected = expected
+  end
+
+  def on_sendable(event)
+    while event.sender.credit > 0 && @sent < @expected
+      msg = Qpid::Proton::Message.new
+      msg.body = "sequence #{@sent}"
+      msg.id = @sent
+      event.sender.send(msg)
+      @sent = @sent + 1
+    end
+  end
+
+  def on_accepted(event)
+    @confirmed = @confirmed + 1
+    if self.finished?
+      puts "#{@expected > 1 ? 'All ' : ''}#{@expected} message#{@expected > 1 ? 's' : ''} confirmed!"
+      event.connection.close
+    end
+  end
+
+  def on_disconnected(event)
+    @sent = @confirmed
+  end
+
+  def finished?
+    @confirmed == @expected
+  end
+
+end
+
+class ExampleReceive < Qpid::Proton::Handler::MessagingHandler
+
+  attr_reader :url
+
+  def initialize(url, expected)
+    super()
+    @url = url
+    @expected = expected
+    @received = 0
+  end
+
+  def on_message(event)
+    if event.message.id.nil? || event.message.id < @received
+      puts "Missing or old message id: id=#{event.message.id}"
+      return
+    end
+    if @expected.zero? || (@received < @expected)
+      puts "Received: #{event.message.body}"
+      @received = @received + 1
+      if finished?
+        event.receiver.close
+        event.connection.close
+      end
+    end
+  end
+
+  def finished?
+    @received == @expected
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/old_example_test.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/old_example_test.rb b/proton-c/bindings/ruby/tests/old_examples/old_example_test.rb
new file mode 100755
index 0000000..16c390d
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/old_example_test.rb
@@ -0,0 +1,85 @@
+#!/usr/bin/enc ruby
+#
+# 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.
+#
+
+require 'minitest/autorun'
+require 'qpid_proton'
+require 'socket'
+
+class ExampleTest < MiniTest::Test
+
+  def run_script(script, port)
+    assert File.exist? script
+    cmd = [RbConfig.ruby, script]
+    cmd += ["-a", ":#{port}/examples"] if port
+    return IO.popen(cmd)
+  end
+
+
+  def assert_output(script, want, port=nil)
+    out = run_script(script, port)
+    assert_equal want, out.read.strip
+  end
+
+  def test_helloworld
+    assert_output("helloworld.rb", "Hello world!", $port)
+  end
+
+  def test_send_recv
+    assert_output("simple_send.rb", "All 100 messages confirmed!", $port)
+    want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+    assert_output("simple_recv.rb", want.strip, $port)
+  end
+
+  def test_client_server
+    want =  <<EOS
+-> 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.
+EOS
+    srv = run_script("server.rb", $port)
+    assert_output("client.rb", want.strip, $port)
+
+  ensure
+    Process.kill :TERM, srv.pid if srv
+  end
+end
+
+# Start the broker before all tests.
+$port = TCPServer.open(0) do |s| s.addr[1]; end # find an unused port
+$broker = spawn("#{RbConfig.ruby} broker.rb -a :#{$port}")
+
+# Wait for the broker to be listening
+deadline = Time.now + 5
+begin
+  TCPSocket.open("", $port).close
+rescue Errno::ECONNREFUSED
+  retry if Time.now < deadline
+  raise
+end
+
+# Kill the broker after all tests
+MiniTest.after_run do
+  Process.kill(:TERM, $broker) if $broker
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/server.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/server.rb b/proton-c/bindings/ruby/tests/old_examples/server.rb
new file mode 100644
index 0000000..9373272
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/server.rb
@@ -0,0 +1,76 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+class Server < Qpid::Proton::Handler::MessagingHandler
+
+  def initialize(url)
+    super()
+    @url = Qpid::Proton::URL.new url
+    @address = @url.path
+    @senders = {}
+  end
+
+  def on_start(event)
+    @container = event.container
+    @conn = @container.connect(:url => @url)
+    @receiver = @container.create_receiver(@conn, :source => @address)
+    @relay = nil
+  end
+
+  def on_connection_opened(event)
+    if event.connection.remote_offered_capabilities &&
+      event.connection.remote_offered_capabilities.contain?("ANONYMOUS-RELAY")
+      @relay = @container.create_sender(@conn, nil)
+    end
+  end
+
+  def on_message(event)
+    msg = event.message
+    puts "<- #{msg.body}"
+    sender = @relay || @senders[msg.reply_to]
+    if sender.nil?
+      sender = @container.create_sender(@conn, :target => msg.reply_to)
+      @senders[msg.reply_to] = sender
+    end
+    reply = Qpid::Proton::Message.new
+    reply.address = msg.reply_to
+    reply.body = msg.body.upcase
+    puts "-> #{reply.body}"
+    reply.correlation_id = msg.correlation_id
+    sender.send(reply)
+  end
+
+  def on_transport_error(event)
+    raise "Connection error: #{event.transport.condition}"
+  end
+end
+
+options = {
+  :address => "localhost:5672/examples",
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: server.rb [options]"
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(Server.new(options[:address])).run()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/simple_recv.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/simple_recv.rb b/proton-c/bindings/ruby/tests/old_examples/simple_recv.rb
new file mode 100644
index 0000000..91cb30c
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/simple_recv.rb
@@ -0,0 +1,58 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+require_relative 'lib/send_and_receive'
+
+class Receiver < ExampleReceive
+
+  def initialize(url, count)
+    super(url, count)
+  end
+
+  def on_start(event)
+    event.container.create_receiver(@url)
+  end
+
+end
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+begin
+  Qpid::Proton::Reactor::Container.new(Receiver.new(options[:address], options[:messages])).run
+rescue Interrupt
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/86de6b58/proton-c/bindings/ruby/tests/old_examples/simple_send.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/old_examples/simple_send.rb b/proton-c/bindings/ruby/tests/old_examples/simple_send.rb
new file mode 100644
index 0000000..13e40f0
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/old_examples/simple_send.rb
@@ -0,0 +1,55 @@
+#--
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#++
+
+require 'qpid_proton'
+require 'optparse'
+
+require_relative 'lib/send_and_receive'
+
+options = {
+  :address => "localhost:5672/examples",
+  :messages => 100,
+}
+
+class SimpleSend < ExampleSend
+
+  def initialize(url, messages)
+    super(url, messages)
+  end
+
+  def on_start(event)
+    event.container.create_sender(url)
+  end
+
+end
+
+OptionParser.new do |opts|
+  opts.banner = "Usage: simple_send.rb [options]"
+
+  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
+    options[:address] = address
+  end
+
+  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
+    OptionParser::DecimalInteger) do |messages|
+    options[:messages] = messages
+  end
+end.parse!
+
+Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run


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


[09/10] qpid-proton git commit: PROTON-1064: [ruby] New Container with native ruby IO

Posted by ac...@apache.org.
PROTON-1064: [ruby] New Container with native ruby IO

Updated all examples and tests to use new container, old Reactor::Container
still in place, will be removed or re-implemented over the new container.

- Based on ConnectionDriver, wrapper for pn_connection_driver_t
- Native polling with standard ruby IO.select()
- Thread-friendly: no blocking IO in C code, all in ruby
- connect/listen use standard TCPSocket/TCPServer
- connect_with/listen_with can use any type of IO socket object


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

Branch: refs/heads/master
Commit: 51cda9d5d50059d1cfe7af57ec4bfb2e2ab1e70e
Parents: 86de6b5
Author: Alan Conway <ac...@redhat.com>
Authored: Tue Oct 31 17:16:08 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 examples/ruby/README.md                         |  54 ++--
 examples/ruby/broker.rb                         |  63 +----
 examples/ruby/client.rb                         |  25 +-
 examples/ruby/direct_recv.rb                    |  35 +--
 examples/ruby/direct_send.rb                    |  37 +--
 examples/ruby/example_test.rb                   | 125 +++++-----
 examples/ruby/helloworld.rb                     |  38 +--
 examples/ruby/helloworld_direct.rb              |  38 +--
 examples/ruby/server.rb                         |  39 ++-
 examples/ruby/simple_recv.rb                    |  36 +--
 examples/ruby/simple_send.rb                    |  33 +--
 proton-c/bindings/ruby/CMakeLists.txt           |   4 +-
 proton-c/bindings/ruby/lib/core/connection.rb   |  67 +++--
 .../bindings/ruby/lib/core/connection_driver.rb |  99 ++++----
 proton-c/bindings/ruby/lib/core/container.rb    | 249 +++++++++++++++++++
 proton-c/bindings/ruby/lib/core/endpoint.rb     |  11 +-
 proton-c/bindings/ruby/lib/core/listener.rb     | 110 ++++++++
 proton-c/bindings/ruby/lib/core/session.rb      |   2 +-
 proton-c/bindings/ruby/lib/core/transport.rb    |  32 +--
 proton-c/bindings/ruby/lib/core/uri.rb          |  50 ++++
 proton-c/bindings/ruby/lib/core/url.rb          |   5 +
 proton-c/bindings/ruby/lib/event/event.rb       |   7 +-
 .../ruby/lib/handler/endpoint_state_handler.rb  |   6 +-
 proton-c/bindings/ruby/lib/qpid_proton.rb       |   8 +
 proton-c/bindings/ruby/lib/reactor/reactor.rb   |   1 +
 proton-c/bindings/ruby/lib/util/condition.rb    |  35 ++-
 proton-c/bindings/ruby/lib/util/engine.rb       |   4 +-
 proton-c/bindings/ruby/lib/util/uri.rb          |  27 --
 .../ruby/tests/test_connection_driver.rb        |   6 +-
 proton-c/bindings/ruby/tests/test_container.rb  | 155 +++++++++---
 proton-c/bindings/ruby/tests/test_tools.rb      |  38 ++-
 31 files changed, 903 insertions(+), 536 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/README.md
----------------------------------------------------------------------
diff --git a/examples/ruby/README.md b/examples/ruby/README.md
index 38cc6ba..8526e29 100644
--- a/examples/ruby/README.md
+++ b/examples/ruby/README.md
@@ -1,32 +1,24 @@
-## What Is The Reactor?
-
-A little outside of the scope of this document, but the reactor is an event source for letting an application know about events in the Proton messaging system. With this set of APIs an application can be register handlers that are notified when a connection is created, a message received, or a session closes.
-
-### Handlers
-
-An application creates **handlers**, objects which provide methods through which the reactor notifies the application's components of events and allows them each to handle the ones in which they are interested (see the Chain Of Responsibility design pattern for more on this idea). There are some pre-defined handlers for responding to incoming message events, outgoing message events, data flow and managing the AMQP endpoints. Look in the **Qpid::Proton::Handlers** package for more details on these classes.
-
-## Simple Reactor Examples
+## Simple Examples
 
 ### The Broker
 
-The reactor examples come with a sample broker which can be used by other examples and which also works as an example itself. For now we'll just start up the broker example and tell it to listen on port 8888:
+The examples come with a sample broker which can be used by other examples and which also works as an example itself. For now we'll just start up the broker example and tell it to listen on port 8888:
 
 ````
-$ ruby ../examples/ruby/reactor/broker.rb  --address=0.0.0.0:8888
-Listening on 0.0.0.0:8888
+$ ruby broker.rb amqp://:8888
+Listening on amqp://:8888
 ````
 
 This example broker will receive messages, create queues as needed, and deliver messages to endpoints.
 
 ### Hello World Using A Broker
 
-Our first example creates an endpoint that sends messages to a queue to which it is subscribed. So it both sends and receives its message in one pass.
+Our first example creates an endpoint that sends messages to a queue to which it is subscribed. So it both sends and receives a message.
 
 To start it, simply run:
 
 ```
-$ ruby ../examples/ruby/reactor/helloworld.rb --address=0.0.0.0:8888 --queue=examples
+$ ruby helloworld.rb //:8888
 Hello world!
 ```
 
@@ -42,12 +34,13 @@ The following events occur while **helloworld.rb** runs:
 
 ### Hello World Without A Broker required
 
-The next example we'll look at will send the classic "Hello world" message to itself directly. This example shows some very fundamental elements of the reactor APIs that you should understand.
+The next example we'll look at will send the classic "Hello world" message to itself directly,
+without going through a broker.
 
 To launch the example:
 
 ```
- $ ruby helloworld_direct.rb --address=0.0.0.0:8888/examples
+ $ ruby helloworld_direct.rb //:9999
  Hello world!
 ```
 
@@ -58,26 +51,25 @@ The direct version takes on the responsibility for listening to incoming connect
  * **on_accepted** - Fired when a message is received.
  * **on_connection_closed** - Fired when an endpoint closes its connection.
 
-## More Complex Reactor Examples
+## More Complex Examples
 
 Now that we've covered the basics with the archetypical hello world app, let's look at some more interesting examples.
 
-There are four example applications that demonstrate how to send and receive messages both directly and through an intermediary, such as a broker:
+The following two client examples send and receive messages to an external broker or server:
 
- * **simple_send.rb** - sends messages to a receiver at a specific address and receives responses via an intermediary,
- * **simple_recv.rb** - receives messages from via an intermediary,
- * **direct_send.rb** - sends messages directly to a receiver and listens for responses itself, and
- * **direct_recv.rb** - receives messages directly.
+ * **simple_send.rb** - connect to a server, send messages to an address
+ * **simple_recv.rb** - connect to a server, receives messages from an address
+
+For example: start `broker.rb`; run `simple_send.rb` to send messages to a
+broker queue; then `simple_recv.rb` to receive the messages from the broker.
 
- Simple send and direct send may, at first, seem to be so similar that you wonder why they're not just the same applciation. And I know for me I was wonder as I wrote the list above why there were two examples. The reason is that **simple_send.rb** uses the intermediary transfer responses to the messages it sends, while **direct_send.rb** uses an *Acceptor* to listen for an process responses.
+The following two examples are *servers* that can be connected to directly, without a broker:
 
- You can use the examples in the follow ways:
+ * **direct_send.rb** - sends messages directly to a receiver and listens for responses itself, and
+ * **direct_recv.rb** - receives messages directly.
 
- ```
- simple_send.rb -> broker <- simple_recv.rb
- simple_send.rb -> direct_recv.rb
- direct_send.rb -> simple_recv.rb
- ```
+For example if you start `direct_recv.rb`, you can connect to it directly with
+`simple_send.rb` vice-versa with `direct_send.rb` and `simple_recv.rb`
 
 In this set of examples we see the following event occurring, in addition to what we've seen before:
 
@@ -91,11 +83,11 @@ The way the broker works is to listen to incoming connections, examine the compo
 
 The components of the broker example include:
  * **Broker** - A class that extends the MessagingHandler class. It accepts incoming connections, manages subscribing them to exchanges, and transfers messages between them.
- * **Exchange** - A class that represents a message queue, tracking what endpoints are subscribed to it.
+ * **MessageQueue** - Distributes messages to subscriptions.
 
 The Broker manages a map connecting a queue address to the instance of Exchange that holds references to the endpoints of interest.
 
-The broker application demonstrates a new set of reactor events:
+The broker application demonstrates a new set of events:
 
  * **on_link_opening** - Fired when a remote link is opened but the local end is not yet open. From this event the broker grabs the address and subscribes the link to an exchange for that address.
  * **on_link_closing** - Fired when a remote link is closed but the local end is still open. From this event the broker grabs the address and unsubscribes the link from that exchange.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/broker.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/broker.rb b/examples/ruby/broker.rb
index 2c37a39..bd461ed 100644
--- a/examples/ruby/broker.rb
+++ b/examples/ruby/broker.rb
@@ -21,11 +21,7 @@ require 'qpid_proton'
 require 'optparse'
 require 'pathname'
 
-def debug(text)
-  print "[#{Time.now.strftime('%s')}] #{text}\n" if $options[:debug]
-end
-
-class Exchange
+class MessageQueue
 
   def initialize(dynamic = false)
     @dynamic = dynamic
@@ -34,30 +30,22 @@ class Exchange
   end
 
   def subscribe(consumer)
-    debug("subscribing #{consumer}")
     @consumers << (consumer)
-    debug(" there are #{@consumers.size} consumers")
   end
 
   def unsubscribe(consumer)
-    debug("unsubscribing #{consumer}")
     if @consumers.include?(consumer)
       @consumers.delete(consumer)
-    else
-      debug(" consumer doesn't exist")
     end
-    debug("  there are #{@consumers.size} consumers")
     @consumers.empty? && (@dynamic || @queue.empty?)
   end
 
   def publish(message)
-    debug("queueing message: #{message.body}")
     @queue << message
     self.dispatch
   end
 
   def dispatch(consumer = nil)
-    debug("dispatching: consumer=#{consumer}")
     if consumer
       c = [consumer]
     else
@@ -69,10 +57,8 @@ class Exchange
   end
 
   def deliver_to(consumers)
-    debug("delivering to #{consumers.size} consumer(s)")
     result = false
     consumers.each do |consumer|
-      debug(" current consumer=#{consumer} credit=#{consumer.credit}")
       if consumer.credit > 0 && !@queue.empty?
         consumer.send(@queue.pop(true))
         result = true
@@ -92,32 +78,23 @@ class Broker < Qpid::Proton::Handler::MessagingHandler
   end
 
   def on_start(event)
-    debug("on_start event")
     @acceptor = event.container.listen(@url)
     print "Listening on #{@url}\n"
   end
 
   def queue(address)
-    debug("fetching queue for #{address}: (there are #{@queues.size} queues)")
     unless @queues.has_key?(address)
-      debug(" creating new queue")
-      @queues[address] = Exchange.new
-    else
-      debug(" using existing queue")
+      @queues[address] = MessageQueue.new
     end
-    result = @queues[address]
-    debug(" returning #{result}")
-    return result
+    @queues[address]
   end
 
   def on_link_opening(event)
-    debug("processing on_link_opening")
-    debug("link is#{event.link.sender? ? '' : ' not'} a sender")
     if event.link.sender?
       if event.link.remote_source.dynamic?
         address = SecureRandom.uuid
         event.link.source.address = address
-        q = Exchange.new(true)
+        q = MessageQueue.new(true)
         @queues[address] = q
         q.subscribe(event.link)
       elsif event.link.remote_source.address
@@ -130,7 +107,6 @@ class Broker < Qpid::Proton::Handler::MessagingHandler
   end
 
   def unsubscribe(link)
-    debug("unsubscribing #{link.source.address}")
     if @queues.has_key?(link.source.address)
       if @queues[link.source.address].unsubscribe(link)
         @queues.delete(link.source.address)
@@ -159,40 +135,21 @@ class Broker < Qpid::Proton::Handler::MessagingHandler
   end
 
   def on_sendable(event)
-    debug("on_sendable event")
     q = self.queue(event.link.source.address)
-    debug(" dispatching #{event.message} to #{q}")
     q.dispatch(event.link)
   end
 
   def on_message(event)
-    debug("on_message event")
     q = self.queue(event.link.target.address)
-    debug(" dispatching #{event.message} to #{q}")
     q.publish(event.message)
   end
 
 end
 
-$options = {
-  :address => "localhost:5672",
-  :debug => false
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: #{Pathname.new(__FILE__).basename} [$options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{$options[:address]}).") do |address|
-    $options[:address] = address
-  end
-
-  opts.on("-d", "--debug", "Enable debugging output (def. #{$options[:debug]})") do
-    $options[:debug] = true
-  end
-
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(Broker.new($options[:address])).run
-rescue Interrupt
+if ARGV.size != 1
+  STDERR.puts "Usage: #{__FILE__} URL
+Start an example broker listening on URL"
+  return 1
 end
+url, = ARGV
+Qpid::Proton::Container.new(Broker.new(url)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/client.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/client.rb b/examples/ruby/client.rb
index 8c38f38..46c1251 100644
--- a/examples/ruby/client.rb
+++ b/examples/ruby/client.rb
@@ -22,15 +22,17 @@ require 'optparse'
 
 class Client < Qpid::Proton::Handler::MessagingHandler
 
-  def initialize(url, requests)
+  def initialize(url, address, requests)
     super()
     @url = url
+    @address = address
     @requests = requests
   end
 
   def on_start(event)
-    @sender = event.container.create_sender(@url)
-    @receiver = event.container.create_receiver(@sender.connection, :dynamic => true)
+    c = event.container.connect(@url)
+    @sender = c.open_sender(@address)
+    @receiver = c.open_receiver({:dynamic => true})
   end
 
   def next_request
@@ -70,13 +72,10 @@ REQUESTS = ["Twas brillig, and the slithy toves",
             "All mimsy were the borogroves,",
             "And the mome raths outgrabe."]
 
-options = {
-  :address => "localhost:5672/examples",
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: client.rb [options]"
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(Client.new(options[:address], REQUESTS)).run
+if ARGV.size != 2
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS
+Connect to URL and send messages to ADDRESS"
+  return 1
+end
+url, address = ARGV
+Qpid::Proton::Container.new(Client.new(url, address, REQUESTS)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/direct_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/direct_recv.rb b/examples/ruby/direct_recv.rb
index 411efba..b2b0ba9 100644
--- a/examples/ruby/direct_recv.rb
+++ b/examples/ruby/direct_recv.rb
@@ -22,15 +22,16 @@ require 'optparse'
 
 class DirectReceive < Qpid::Proton::Handler::MessagingHandler
 
-  def initialize(url, expected)
+  def initialize(url, address, count)
     super()
     @url = url
-    @expected = expected
+    @address = address
+    @expected = count
     @received = 0
   end
 
   def on_start(event)
-    @acceptor = event.container.listen(@url)
+    event.container.listen(@url)
   end
 
   def on_message(event)
@@ -38,29 +39,17 @@ class DirectReceive < Qpid::Proton::Handler::MessagingHandler
       puts "Received: #{event.message.body}"
       @received = @received + 1
       if @received == @expected
-        event.connection.close
-        @acceptor.close
+        event.container.stop
       end
     end
   end
 end
 
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
+unless (2..3).include? ARGV.size
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS [COUNT]
+Listen on URL and receive COUNT messages from ADDRESS"
+  return 1
+end
+url, address, count = ARGV
+Qpid::Proton::Container.new(DirectReceive.new(url, address, count || 10)).run
 
-Qpid::Proton::Reactor::Container.new(DirectReceive.new(options[:address], options[:messages])).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/direct_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/direct_send.rb b/examples/ruby/direct_send.rb
index ed679aa..e54e1ab 100644
--- a/examples/ruby/direct_send.rb
+++ b/examples/ruby/direct_send.rb
@@ -20,23 +20,19 @@
 require 'qpid_proton'
 require 'optparse'
 
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
+class DirectSend < Qpid::Proton::Handler::MessagingHandler
 
-class SimpleSend < Qpid::Proton::Handler::MessagingHandler
-
-  def initialize(url, expected)
+  def initialize(url, address, expected)
     super()
     @url = url
+    @address = address
     @sent = 0
     @confirmed = 0
     @expected = expected
   end
 
   def on_start(event)
-    @acceptor = event.container.listen(@url)
+*co    event.container.listen(@url)
   end
 
   def on_sendable(event)
@@ -51,26 +47,15 @@ class SimpleSend < Qpid::Proton::Handler::MessagingHandler
     @confirmed = @confirmed + 1
     if @confirmed == @expected
       puts "All #{@expected} messages confirmed!"
-      event.connection.close
+      event.container.stop
     end
   end
 end
 
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run
-rescue Interrupt => error
-  puts "ERROR: #{error}"
+unless (2..3).include? ARGV.size
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS [COUNT]
+Listen on URL and send COUNT messages to ADDRESS"
+  return 1
 end
+url, address, count = ARGV
+Qpid::Proton::Container.new(DirectSend.new(url, address, count || 10)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/example_test.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/example_test.rb b/examples/ruby/example_test.rb
index 12ab784..bcb6aa6 100755
--- a/examples/ruby/example_test.rb
+++ b/examples/ruby/example_test.rb
@@ -1,4 +1,4 @@
-#!/usr/bin/enc ruby
+#!/usr/bin/env ruby
 #
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
@@ -22,56 +22,36 @@ require 'minitest/autorun'
 require 'qpid_proton'
 require 'socket'
 
-class ExampleTest < MiniTest::Test
-
-  def run_script(script, port)
-    cmd = [RbConfig.ruby, script]
-    cmd += ["-a", ":#{port}/examples"] if port
-    return IO.popen(cmd)
+# Wait for the broker to be listening
+def wait_for(url, timeout = 5)
+  deadline = Time.now + 5
+  begin
+    TCPSocket.open("", URI(url).port).close
+  rescue Errno::ECONNREFUSED
+    retry if Time.now < deadline
+    raise
   end
+end
 
-  def assert_output(script, want, port=nil)
-    out = run_script(script, port)
-    assert_equal want, out.read.strip
-  end
+# URL with an unused port
+def test_url()
+  "amqp://:#{TCPServer.open(0) { |s| s.addr[1] }}"
+end
 
-  def test_helloworld
-    assert_output("helloworld.rb", "Hello world!", $port)
-  end
 
-  def test_send_recv
-    assert_output("simple_send.rb", "All 100 messages confirmed!", $port)
-    want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
-    assert_output("simple_recv.rb", want.strip, $port)
-  end
+class ExampleTest < MiniTest::Test
 
-  def test_direct_recv
-    TestPort.new do |tp|
-      p = run_script("direct_recv.rb", tp.port)
-      wait_port tp.port
-      assert_output("simple_send.rb", "All 100 messages confirmed!", tp.port)
-      want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
-      assert_equal(want.strip, p.read.strip)
-    end
+  def run_script(*args)
+    return IO.popen([ RbConfig.ruby ] + args.map { |a| a.to_s })
   end
 
-  def test_direct_send
-    TestPort.new do |tp|
-      p = run_script("direct_send.rb", tp.port)
-      wait_port tp.port
-      want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
-      assert_output("simple_recv.rb", want.strip, $port)
-      assert_equal("All 100 messages confirmed!", p.read.strip)
-    end
+  def assert_output(want, *args)
+    out = run_script(*args)
+    assert_equal(want, out.read.strip)
   end
 
-  def test_direct_send
-    TestPort.start_wait do |port|
-      p = run_script("direct_recv.rb", port)
-      assert_output("simple_send.rb", "All 100 messages confirmed!", port)
-      want = (0..99).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
-      assert_equal(want.strip, p.read.strip)
-    end
+  def test_helloworld
+    assert_output("Hello world!", "helloworld.rb", $url, __method__)
   end
 
   def test_client_server
@@ -85,28 +65,57 @@ class ExampleTest < MiniTest::Test
 -> And the mome raths outgrabe.
 <- AND THE MOME RATHS OUTGRABE.
 EOS
-    srv = run_script("server.rb", $port)
-    assert_output("client.rb", want.strip, $port)
-
+    server = run_script("server.rb", $url, __method__)
+    assert_output(want.strip, "client.rb", $url, __method__)
   ensure
-    Process.kill :TERM, srv.pid if srv
+    Process.kill :TERM, server.pid if server
   end
-end
 
-# Start the broker before all tests.
-$port = TCPServer.open(0) do |s| s.addr[1]; end # find an unused port
-$broker = spawn("#{RbConfig.ruby} reactor/broker.rb -a :#{$port}")
+  def test_send_recv
+    assert_output("All 10 messages confirmed!", "simple_send.rb", $url, __method__)
+    want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+    assert_output(want.strip, "simple_recv.rb", $url, __method__)
+  end
 
-# Wait for the broker to be listening
-deadline = Time.now + 5
-begin
-  TCPSocket.open("", $port).close
-rescue Errno::ECONNREFUSED
-  retry if Time.now < deadline
-  raise
+  def test_helloworld_direct
+    url = test_url
+    assert_output("Hello world!", "helloworld_direct.rb", url, __method__)
+  end
+
+  def test_direct_recv
+    url = test_url
+    p = run_script("direct_recv.rb", url, __method__)
+    wait_for url
+    assert_output("All 10 messages confirmed!", "simple_send.rb", url, __method__)
+    want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+    assert_equal(want.strip, p.read.strip)
+  end
+
+  def test_direct_send
+    url = test_url
+    p = run_script("direct_send.rb", url, __method__)
+    wait_for url
+    want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+    assert_output(want.strip, "simple_recv.rb", url, __method__)
+    assert_equal("All 10 messages confirmed!", p.read.strip)
+  end
+
+  def test_direct_send
+    url = test_url
+    p = run_script("direct_recv.rb", url, __method__)
+    wait_for url
+    assert_output("All 10 messages confirmed!", "simple_send.rb", url, __method__)
+    want = (0..9).reduce("") { |x,y| x << "Received: sequence #{y}\n" }
+    assert_equal(want.strip, p.read.strip)
+  end
 end
 
+# Start the broker before all tests.
+$url = test_url
+$broker = IO.popen([RbConfig.ruby, 'broker.rb', $url])
+wait_for $url
+
 # Kill the broker after all tests
 MiniTest.after_run do
-  Process.kill(:TERM, $broker) if $broker
+  Process.kill(:TERM, $broker.pid) if $broker
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/helloworld.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/helloworld.rb b/examples/ruby/helloworld.rb
index 9b02e8a..2b2d4f5 100644
--- a/examples/ruby/helloworld.rb
+++ b/examples/ruby/helloworld.rb
@@ -22,16 +22,15 @@ require 'optparse'
 
 class HelloWorld < Qpid::Proton::Handler::MessagingHandler
 
-  def initialize(server, address)
+  def initialize(url, address)
     super()
-    @server = server
-    @address = address
+    @url, @address = url, address
   end
 
   def on_start(event)
-    conn = event.container.connect(:address => @server)
-    event.container.create_sender(conn, :target => @address)
-    event.container.create_receiver(conn, :source => @address)
+    conn = event.container.connect(@url)
+    conn.open_sender(@address)
+    conn.open_receiver(@address)
   end
 
   def on_sendable(event)
@@ -51,23 +50,10 @@ class HelloWorld < Qpid::Proton::Handler::MessagingHandler
   end
 end
 
-options = {
-  :address => "localhost:5672",
-  :queue => "examples"
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: helloworld_direct.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-q", "--queue=QUEUE", "Send messages to QUEUE (def. #{options[:queue]})") do |queue|
-    options[:queue] = queue
-  end
-
-end.parse!
-
-hw = HelloWorld.new(options[:address], "examples")
-Qpid::Proton::Reactor::Container.new(hw).run
+if ARGV.size != 2
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS
+Connect to URL, send a message to ADDRESS and receive it back"
+  return 1
+end
+url, address = ARGV
+Qpid::Proton::Container.new(HelloWorld.new(url, address)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/helloworld_direct.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/helloworld_direct.rb b/examples/ruby/helloworld_direct.rb
index e98cc1f..dab368b 100644
--- a/examples/ruby/helloworld_direct.rb
+++ b/examples/ruby/helloworld_direct.rb
@@ -20,22 +20,19 @@
 require 'qpid_proton'
 require 'optparse'
 
-options = {
-  :address => "localhost:5672/examples",
-}
-
 class HelloWorldDirect < Qpid::Proton::Handler::MessagingHandler
 
   include Qpid::Proton::Util::Wrapper
 
-  def initialize(url)
+  def initialize(url, address)
     super()
-    @url = url
+    @url, @address = url, address
   end
 
   def on_start(event)
-    @acceptor = event.container.listen(@url)
-    event.container.create_sender(@url)
+    event.container.listen(@url)
+    c = event.container.connect(@url) # Connect to self!
+    c.open_sender(@address)
   end
 
   def on_sendable(event)
@@ -50,25 +47,14 @@ class HelloWorldDirect < Qpid::Proton::Handler::MessagingHandler
   end
 
   def on_accepted(event)
-    event.connection.close
-  end
-
-  def on_connection_closed(event)
-    @acceptor.close
+    event.container.stop
   end
-
 end
 
-OptionParser.new do |opts|
-  opts.banner = "Usage: helloworld_direct.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-end.parse!
-
-begin
-  Qpid::Proton::Reactor::Container.new(HelloWorldDirect.new(options[:address])).run
-rescue Interrupt => error
+if ARGV.size != 2
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS
+Listen on and connect to URL (connect to self), send a message to ADDRESS and receive it back"
+  return 1
 end
+url, address = ARGV
+Qpid::Proton::Container.new(HelloWorldDirect.new(url, address)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/server.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/server.rb b/examples/ruby/server.rb
index 9373272..87eba99 100644
--- a/examples/ruby/server.rb
+++ b/examples/ruby/server.rb
@@ -22,35 +22,31 @@ require 'optparse'
 
 class Server < Qpid::Proton::Handler::MessagingHandler
 
-  def initialize(url)
+  def initialize(url, address)
     super()
-    @url = Qpid::Proton::URL.new url
-    @address = @url.path
+    @url = url
+    @address = address
     @senders = {}
   end
 
   def on_start(event)
-    @container = event.container
-    @conn = @container.connect(:url => @url)
-    @receiver = @container.create_receiver(@conn, :source => @address)
+    c = event.container.connect(@url)
+    c.open_receiver(@address)
     @relay = nil
   end
 
   def on_connection_opened(event)
     if event.connection.remote_offered_capabilities &&
-      event.connection.remote_offered_capabilities.contain?("ANONYMOUS-RELAY")
-      @relay = @container.create_sender(@conn, nil)
+        event.connection.remote_offered_capabilities.contain?("ANONYMOUS-RELAY")
+      @relay = event.connection.open_sender({:target => nil})
     end
   end
 
   def on_message(event)
     msg = event.message
+    return unless msg.reply_to  # Not a request message
     puts "<- #{msg.body}"
-    sender = @relay || @senders[msg.reply_to]
-    if sender.nil?
-      sender = @container.create_sender(@conn, :target => msg.reply_to)
-      @senders[msg.reply_to] = sender
-    end
+    sender = @relay || (@senders[msg.reply_to] ||= event.connection.open_sender(msg.reply_to))
     reply = Qpid::Proton::Message.new
     reply.address = msg.reply_to
     reply.body = msg.body.upcase
@@ -64,13 +60,10 @@ class Server < Qpid::Proton::Handler::MessagingHandler
   end
 end
 
-options = {
-  :address => "localhost:5672/examples",
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: server.rb [options]"
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") { |address| options[:address] = address }
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(Server.new(options[:address])).run()
+if ARGV.size != 2
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS
+Server listening on URL, reply to messages to ADDRESS"
+  return 1
+end
+url, address = ARGV
+Qpid::Proton::Container.new(Server.new(url, address)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/simple_recv.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/simple_recv.rb b/examples/ruby/simple_recv.rb
index 47b21ed..d1a9607 100644
--- a/examples/ruby/simple_recv.rb
+++ b/examples/ruby/simple_recv.rb
@@ -20,17 +20,19 @@
 require 'qpid_proton'
 require 'optparse'
 
-class Receiver < Qpid::Proton::Handler::MessagingHandler
+class SimpleReceive < Qpid::Proton::Handler::MessagingHandler
 
-  def initialize(url, count)
+  def initialize(url, address, count)
     super()
     @url = url
+    @address = address
     @expected = count
     @received = 0
   end
 
   def on_start(event)
-    event.container.create_receiver(@url)
+    c = event.container.connect(@url)
+    c.open_receiver(@address)
   end
 
   def on_message(event)
@@ -42,28 +44,14 @@ class Receiver < Qpid::Proton::Handler::MessagingHandler
       end
     end
   end
-
 end
 
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
+unless (2..3).include? ARGV.size
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS [COUNT]}
+Connect to URL and receive COUNT messages from ADDRESS"
+  return 1
+end
+url, address, count = ARGV
 
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
+Qpid::Proton::Container.new(SimpleReceive.new(url, address, count || 10)).run
 
-begin
-  Qpid::Proton::Reactor::Container.new(Receiver.new(options[:address], options[:messages])).run
-rescue Interrupt
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/examples/ruby/simple_send.rb
----------------------------------------------------------------------
diff --git a/examples/ruby/simple_send.rb b/examples/ruby/simple_send.rb
index 38857f3..25fc56c 100644
--- a/examples/ruby/simple_send.rb
+++ b/examples/ruby/simple_send.rb
@@ -20,23 +20,20 @@
 require 'qpid_proton'
 require 'optparse'
 
-options = {
-  :address => "localhost:5672/examples",
-  :messages => 100,
-}
-
 class SimpleSend < Qpid::Proton::Handler::MessagingHandler
 
-  def initialize(url, expected)
+  def initialize(url, address, expected)
     super()
     @url = url
+    @address = address
     @sent = 0
     @confirmed = 0
     @expected = expected
   end
 
   def on_start(event)
-    event.container.create_sender(@url)
+    c = event.container.connect(@url)
+    c.open_sender(@address)
   end
 
   def on_sendable(event)
@@ -54,20 +51,12 @@ class SimpleSend < Qpid::Proton::Handler::MessagingHandler
       event.connection.close
     end
   end
-
 end
 
-OptionParser.new do |opts|
-  opts.banner = "Usage: simple_send.rb [options]"
-
-  opts.on("-a", "--address=ADDRESS", "Send messages to ADDRESS (def. #{options[:address]}).") do |address|
-    options[:address] = address
-  end
-
-  opts.on("-m", "--messages=COUNT", "The number of messages to send (def. #{options[:messages]}",
-    OptionParser::DecimalInteger) do |messages|
-    options[:messages] = messages
-  end
-end.parse!
-
-Qpid::Proton::Reactor::Container.new(SimpleSend.new(options[:address], options[:messages])).run
+unless (2..3).include? ARGV.size
+  STDERR.puts "Usage: #{__FILE__} URL ADDRESS [COUNT]}
+Connect to URL and send COUNT messages to ADDRESS"
+  return 1
+end
+url, address, count = ARGV
+Qpid::Proton::Container.new(SimpleSend.new(url, address, count || 10)).run

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/CMakeLists.txt b/proton-c/bindings/ruby/CMakeLists.txt
index 46b7ba8..c00fd3d 100644
--- a/proton-c/bindings/ruby/CMakeLists.txt
+++ b/proton-c/bindings/ruby/CMakeLists.txt
@@ -45,7 +45,7 @@ set_target_properties(cproton-ruby
 
 ##  Make a gem
 
-file(GLOB_RECURSE RUBY_SRC RELATIVE . *.rb *.rdoc)
+file(GLOB_RECURSE RUBY_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.rb *.rdoc)
 
 find_program(GEM_EXE gem DOC "Program to build and install ruby gem packages")
 mark_as_advanced(GEM_EXE)
@@ -133,7 +133,7 @@ if (YARD_EXE)
   add_custom_command(
     OUTPUT ${bin}/doc
     WORKING_DIRECTORY ${src}
-    COMMAND ${YARD_EXE} -q -o ${bin}/doc -b ${bin}/.yardoc --no-private -r README.rdoc
+    COMMAND ${YARD_EXE} -q -o ${bin}/doc -b ${bin}/.yardoc --no-progress --no-private -r README.rdoc
     DEPENDS ${RUBY_SRC}
     )
   add_custom_target(docs-ruby DEPENDS ${bin}/doc)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/connection.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/connection.rb b/proton-c/bindings/ruby/lib/core/connection.rb
index 6caf589..0c878d4 100644
--- a/proton-c/bindings/ruby/lib/core/connection.rb
+++ b/proton-c/bindings/ruby/lib/core/connection.rb
@@ -23,21 +23,17 @@ module Qpid::Proton
   class Connection < Endpoint
 
     protected
-    PROTON_METHOD_PREFIX = "pn_connection"
     include Util::SwigHelper
+    PROTON_METHOD_PREFIX = "pn_connection"
 
     public
 
     # @!attribute hostname
-    #
-    # @return [String] The AMQP hostname for the connection.
-    #
+    #   @return [String] The AMQP hostname for the connection.
     proton_accessor :hostname
 
     # @!attribute user
-    #   The user name for authentication.
-    #
-    #   @return [String] the user name
+    #   @return [String] User name used for authentication (outgoing connection) or the authenticated user name (incoming connection)
     proton_accessor :user
 
     # @private
@@ -45,6 +41,7 @@ module Qpid::Proton
 
     # @private
     attr_accessor :overrides
+    # @private
     attr_accessor :session_policy
 
     # @private
@@ -68,12 +65,11 @@ module Qpid::Proton
     def initialize(impl = Cproton.pn_connection)
       super()
       @impl = impl
-      @offered_capabilities = nil
-      @desired_capabilities = nil
-      @properties = nil
       @overrides = nil
       @collector = nil
       @session_policy = nil
+      @link_count = 0
+      @link_prefix = ""
       self.class.store_instance(self, :pn_connection_attachments)
     end
 
@@ -195,18 +191,34 @@ module Qpid::Proton
 
     # Open the local end of the connection.
     #
-    # @option options [String] :container_id Unique AMQP container ID, defaults to a UUID
-    # @option options [String] :link_prefix Prefix for generated link names, default is container_id
-    #
-    def open(options={})
-      object_to_data(@offered_capabilities, Cproton.pn_connection_offered_capabilities(@impl))
-      object_to_data(@desired_capabilities, Cproton.pn_connection_desired_capabilities(@impl))
-      object_to_data(@properties, Cproton.pn_connection_properties(@impl))
-      cid = options[:container_id] || SecureRandom.uuid
-      Cproton.pn_connection_set_container(@impl, cid)
-      @link_prefix = options[:link_prefix] || cid
-      @link_prefix = SecureRandom.uuid if !@link_prefix || @link_prefix.empty?
-      @link_count = 0
+    # @option opts [MessagingHandler] :handler handler for events related to this connection.
+    # @option opts [String] :user user-name for authentication.
+    # @option opts [String] :password password for authentication.
+    # @option opts [Numeric] :idle_timeout seconds before closing an idle connection
+    # @option opts [Boolean] :sasl_enabled Enable or disable SASL.
+    # @option opts [Boolean] :sasl_allow_insecure_mechs Allow mechanisms that disclose clear text
+    #   passwords, even over an insecure connection.
+    # @option opts [String] :sasl_allowed_mechs the allowed SASL mechanisms for use on the connection.
+    # @option opts [String] :container_id AMQP container ID, normally provided by {Container}
+    #
+    def open(opts={})
+      return if local_active?
+      apply opts
+      Cproton.pn_connection_open(@impl)
+    end
+
+    # @private
+    def apply opts
+      # NOTE: Only connection options are set here. Transport options are set
+      # with {Transport#apply} from the connection_driver (or in
+      # on_connection_bound if not using a connection_driver)
+      Cproton.pn_connection_set_container(@impl, opts[:container_id] || SecureRandom.uuid)
+      Cproton.pn_connection_set_user(@impl, opts[:user]) if opts[:user]
+      Cproton.pn_connection_set_password(@impl, opts[:password]) if opts[:password]
+      @link_prefix = opts[:link_prefix] || container_id
+      object_to_data(opts[:offered_capabilities], Cproton.pn_connection_offered_capabilities(@impl))
+      object_to_data(opts[:desired_capabilities], Cproton.pn_connection_desired_capabilities(@impl))
+      object_to_data(opts[:properties], Cproton.pn_connection_properties(@impl))
       Cproton.pn_connection_open(@impl)
     end
 
@@ -220,8 +232,11 @@ module Qpid::Proton
     # Once this operation has completed, the #LOCAL_CLOSED state flag will be
     # set.
     #
-    def close
-      self._update_condition
+    def close(error = nil)
+      if error
+        @condition = Condition.make error
+        self._update_condition
+      end
       Cproton.pn_connection_close(@impl)
     end
 
@@ -257,10 +272,10 @@ module Qpid::Proton
     end
 
     # Open a sender on the default_session
-    def open_sender(*args, &block) default_session.open_sender(*args, &block) end
+    def open_sender(opts = {}) default_session.open_sender(opts) end
 
     # Open a  on the default_session
-    def open_receiver(*args, &block) default_session.open_receiver(*args, &block) end
+    def open_receiver(opts = {}) default_session.open_receiver(opts) end
 
     # Returns the first session from the connection that matches the specified
     # state mask.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/connection_driver.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/connection_driver.rb b/proton-c/bindings/ruby/lib/core/connection_driver.rb
index b5b38ac..6c85659 100644
--- a/proton-c/bindings/ruby/lib/core/connection_driver.rb
+++ b/proton-c/bindings/ruby/lib/core/connection_driver.rb
@@ -36,23 +36,30 @@ module Qpid
       #
       # @param io [#read_nonblock, #write_nonblock] An {IO} or {IO}-like object that responds
       #   to #read_nonblock and #write_nonblock.
-      # @param handler [MessagingHandler] The handler to be invoked for AMQP events
-      #
-      def initialize io, handler=nil
+      # @param opts [Hash] See {Connection#open} - transport options are set here,
+      # remaining options
+      # @pram server [Bool] If true create a server (incoming) connection
+      def initialize(io, opts = {}, server=false)
         @impl = Cproton.pni_connection_driver or raise RuntimeError, "cannot create connection driver"
         @io = io
-        @handler = handler || Handler::MessagingHandler.new # Default handler for default behaviour
-        @rbuf = ""                                          # String to re-use as read buffer
+        @handler = opts[:handler] || Handler::MessagingHandler.new # Default handler if missing
+        @rbuf = ""              # String to re-use as read buffer
+        connection.apply opts
+        transport.set_server if server
+        transport.apply opts
       end
 
-      # @return [MessagingHandler]
       attr_reader :handler
 
       # @return [Connection]
-      def connection() Connection.wrap(Cproton.pni_connection_driver_connection(@impl)); end
+      def connection()
+        @connection ||= Connection.wrap(Cproton.pni_connection_driver_connection(@impl))
+      end
 
       # @return [Transport]
-      def transport() Transport.wrap(Cproton.pni_connection_driver_transport(@impl)); end
+      def transport()
+        @transport ||= Transport.wrap(Cproton.pni_connection_driver_transport(@impl))
+      end
 
       # @return [IO] Allows ConnectionDriver to be passed directly to {IO#select}
       def to_io() @io; end
@@ -63,21 +70,15 @@ module Qpid
       # @return [Bool] True if the driver has data to write
       def can_write?() Cproton.pni_connection_driver_write_size(@impl) > 0; end
 
-      # True if read and write sides of the IO are closed. Note this does not imply
-      # {#finished?} since there may still be events to dispatch.
-      def closed?
-        Cproton.pn_connection_driver_read_closed(@impl) &&
-          Cproton.pn_connection_driver_read_closed(@impl)
-      end
-
-      # True if the ConnectionDriver has nothing left to do: {#closed?} and
-      # there are no more events to dispatch.
+      # True if the ConnectionDriver has nothing left to do: both sides of the
+      # transport are closed and there are no events to dispatch.
       def finished?() Cproton.pn_connection_driver_finished(@impl); end
 
       # Dispatch available events, call the relevant on_* methods on the {#handler}.
       def dispatch(extra_handlers = nil)
         extra_handlers ||= []
         while event = Event::Event.wrap(Cproton.pn_connection_driver_next_event(@impl))
+          pre_dispatch(event)
           event.dispatch(@handler)
           extra_handlers.each { |h| event.dispatch h }
         end
@@ -90,13 +91,12 @@ module Qpid
         return if size <= 0
         @io.read_nonblock(size, @rbuf) # Use the same string rbuf for reading each time
         Cproton.pni_connection_driver_read_copy(@impl, @rbuf) unless @rbuf.empty?
-        rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
-          # Try again later.
-        rescue EOFError         # EOF is not an error
-          Cproton.pn_connection_driver_read_close(@impl)
-        rescue IOError => e     # IOError is passed to the transport
-          error "read: #{e}"
-          Cproton.pn_connection_driver_read_close(@impl)
+      rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
+        # Try again later.
+      rescue EOFError         # EOF is not an error
+        close_read
+      rescue IOError, SystemCallError => e     #  is passed to the transport
+        close e
       end
 
       # Write to IO without blocking.
@@ -106,9 +106,8 @@ module Qpid
         Cproton.pn_connection_driver_write_done(@impl, n) if n > 0
       rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
         # Try again later.
-      rescue IOError => e
-        error "write: #{e}"
-        Cproton.pn_connection_driver_write_close(@impl)
+      rescue IOError, SystemCallError => e
+        close e
       end
 
       # Generate timed events and IO, for example idle-timeout and heart-beat events.
@@ -142,41 +141,37 @@ module Qpid
         return next_tick
       end
 
-      # Close the read side of the IO with optional error.
-      # @param e [#to_s] Non-nil error will call {#handler}.on_transport_error on next {#dispatch}
-      def close_read(e=nil)
-          @io.close_read
-          error(e)
-          Cproton.pn_connection_driver_read_close(@impl)
+      # Close the read side of the transport
+      def close_read
+        return if Cproton.pn_connection_driver_read_closed(@impl)
+        Cproton.pn_connection_driver_read_close(@impl)
+        @io.close_read
       end
 
-      # Close the write side of the IO with optional error
-      # @param e [#to_s] Non-nil error will call {#handler}.on_transport_error on next {#dispatch}
-      def close_write(e=nil)
-          @io.close_write
-          error(e)
-          Cproton.pn_connection_driver_write_close(@impl)
+      # Close the write side of the transport
+      def close_write
+        return if Cproton.pn_connection_driver_write_closed(@impl)
+        Cproton.pn_connection_driver_write_close(@impl)
+        @io.close_write
       end
 
       # Close both sides of the IO with optional error
-      # @param e [#to_s] Non-nil error will call {#handler}.on_transport_error on next {#dispatch}
-      def close(e=nil)
-        if !closed?
-          close_read(e)
-          close_write(e)
+      # @param error [Condition] If non-nil pass to {#handler}.on_transport_error on next {#dispatch}
+      # Note `error` can be any value accepted by [Condition##make]
+      def close(error=nil)
+        if error
+          cond = Condition.make(error, "proton:io")
+          Cproton.pn_connection_driver_errorf(@impl, cond.name, "%s", cond.description)
         end
+        close_read
+        close_write
       end
 
-      def to_s
-        transport = Cproton.pni_connection_driver_tranport(@impl)
-        return "#<#{self.class.name}[#{transport}]:#{@io}>"
-      end
+      protected
 
-      private
+      # Override in subclass to add event context
+      def pre_dispatch(event) event; end
 
-      def error(e)
-        Cproton.pn_connection_driver_errorf(@impl, "proton:io", "%s", e.to_s) if e
-      end
     end
   end
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/container.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/container.rb b/proton-c/bindings/ruby/lib/core/container.rb
new file mode 100644
index 0000000..29df51b
--- /dev/null
+++ b/proton-c/bindings/ruby/lib/core/container.rb
@@ -0,0 +1,249 @@
+#--
+# 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.
+#++
+
+require 'thread'
+require 'set'
+require_relative 'listener'
+
+module Qpid::Proton
+
+  # An AMQP container manages a set of {Connection}s which contain {#Sender} and
+  # {#Receiver} links to transfer messages.
+  #
+  # TODO aconway 2017-10-26: documentthreading/dispatch role
+  #
+  # Usually, each AMQP client or server process has a single container for
+  # all of its connections and links.
+  class Container
+    private
+
+    def amqp_uri(s) Qpid::Proton::amqp_uri s; end
+
+    class ConnectionDriver < Qpid::Proton::ConnectionDriver
+      def initialize container, io, opts, server=false
+        super io, opts, server
+        @container = container
+      end
+
+      def final() end
+      def pre_dispatch(event) event.container = @container; end
+    end
+
+    public
+
+    # Create a new Container
+    #
+    # @param opts [Hash] Options
+    # @option opts [String] :id A unique ID for this container. Defaults to a random UUID.
+    # @option opts [MessagingHandler] :handler Default handler for connections
+    #   that do not have their own handler (see {#connect} and {#listen})
+    #
+    #   @note In a multi-threaded, multi-connection container the default
+    #   handler can be called concurrently for different connections. An
+    #   individual handler attached to a single connection is never called
+    #   concurrently, so that is the recommended approach for multi-threading.
+    def initialize(opts = {})
+      opts = { :handler => opts } unless opts.is_a? Hash # Allow handler as only parameter
+      opts = { :handler => opts } if opts.is_a? String   # Allow ID as only parameter option
+      @handler = opts[:handler]
+      @id = String.new(opts[:id] || SecureRandom.uuid).freeze
+      @work = Queue.new
+      @work.push self           # Let the first #run thread select
+      @wake = IO.pipe
+      @dummy = ""               # Dummy buffer for draining wake pipe
+      @lock = Mutex.new
+      @selectables = Set.new    # ConnectionDrivers and Listeners
+      @auto_stop = true
+      @active = 0               # activity (connection, listener) counter for auto_stop
+      @running = 0              # concurrent calls to #run
+    end
+
+    # @return [String] Unique identifier for this container
+    attr_reader :id
+
+    # Open an AMQP connection.
+    #
+    # @param url [String, URI] Open a {TCPSocket} to url.host, url.port.
+    # url.scheme must be "amqp" or "amqps", url.scheme.nil? is treated as "amqp"
+    # url.user, url.password are used as defaults if opts[:user], opts[:password] are nil
+    # @option (see Connection#open)
+    # @return [Connection] The new AMQP connection
+    #
+    def connect(url, opts = {})
+      url = amqp_uri(url)
+      opts[:user] ||= url.user
+      opts[:password] ||= url.password
+      # TODO aconway 2017-10-26: Use SSL for amqps URLs
+      connect_io(TCPSocket.new(url.host, url.port), opts)
+    end
+
+    # Open an AMQP protocol connection on an existing {IO} object
+    # @param io [IO] An existing {IO} object, e.g. a {TCPSocket}
+    # @option (see Connection#open)
+    def connect_io(io, opts = {})
+      cd = connection_driver(io, opts)
+      cd.connection.open()
+      add(cd).connection
+    end
+
+    # Listen for incoming AMQP connections
+    #
+    # @param url [String,URI] Listen on host:port of the AMQP URL
+    # @param handler [ListenHandler] A {ListenHandler} object that will be called
+    # with events for this listener and can generate a new set of options for each one.
+    # @return [Listener] The AMQP listener.
+    def listen(url, handler=ListenHandler.new)
+      url = amqp_uri(url)
+      # TODO aconway 2017-11-01: amqps
+      listen_io(TCPServer.new(url.host, url.port), handler)
+    end
+
+    # Listen for incoming AMQP connections on an existing server socket.
+    # @param io A server socket, for example a {TCPServer}
+    # @param handler [ListenHandler] Handler for events from this listener
+    def listen_io(io, handler=ListenHandler.new)
+      add(Listener.new(io, handler))
+    end
+
+    # Run the container: wait for IO activity, dispatch events to handlers.
+    #
+    # More than one thread can call {#run} concurrently, the container will use
+    # all the {#run} ,threads as a pool to handle multiple connections
+    # concurrently.  The container ensures that handler methods for a single
+    # connection (or listener) instance are serialized, even if the container
+    # has multiple threads.
+    #
+    def run()
+      @lock.synchronize { @running += 1 }
+
+      unless @on_start
+        @on_start = true
+        # TODO aconway 2017-10-28: proper synthesized event for on_start
+        event = Class.new do
+          def initialize(c) @container = c; end
+          attr_reader :container
+        end.new(self)
+        @handler.on_start(event) if @handler && @handler.respond_to?(:on_start)
+      end
+
+      while x = @work.pop
+        case x
+        when Container then
+          # Only one thread can select at a time
+          r, w = [@wake[0]], []
+          @lock.synchronize do
+            @selectables.each do |s|
+              r << s if s.send :can_read?
+              w << s if s.send :can_write?
+            end
+          end
+          r, w = IO.select(r, w)
+          selected = Set.new(r).merge(w)
+          if selected.delete?(@wake[0]) # Drain the wake pipe
+            begin
+              @wake[0].read_nonblock(256, @dummy) while true
+            rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
+            end
+          end
+          @lock.synchronize do
+            if @stop_all
+              selected = @selectables
+              @selectables = Set.new
+              selected.each { |s| s.close }
+              @work << nil if selected.empty? # Already idle, initiate stop now
+              @stop_all = false
+            else
+              @selectables.subtract(selected)
+            end
+          end
+          # Move selected items to the work queue for serialized processing.
+          @lock.synchronize { @selectables.subtract(selected) }
+          selected.each { |s| @work << s } # Queue up all the work
+          @work << self                    # Allow another thread to select()
+        when ConnectionDriver then
+          x.process
+          rearm x
+        when Listener then
+          io, opts = x.send :process
+          add(connection_driver(io, opts, true)) if io
+          rearm x
+        end
+        # TODO aconway 2017-10-26: scheduled tasks
+      end
+    ensure
+      @running -= 1
+      if @running > 0 # Signal the next #run thread that we are stopping
+        @work << nil
+        wake
+      end
+    end
+
+    # @!attribute auto_stop [rw]
+    #   @return [Bool] With auto_stop enabled, all calls to {#run} will return when the
+    #   container's last activity (connection, listener or scheduled event) is
+    #   closed/completed. With auto_stop disabled {#run} does not return.
+    def auto_stop=(enabled) @lock.synchronize { @auto_stop=enabled }; wake; end
+    def auto_stop() @lock.synchronize { @auto_stop }; end
+
+    # Enable {#auto_stop} and close all connections and listeners with error.
+    # {#stop} returns immediately, calls to {#run} will return when all activity is finished.
+    # @param error [Condition] If non-nil pass to {#handler}.on_error
+    # Note `error` can be any value accepted by [Condition##make]
+    def stop(error=nil)
+      @lock.synchronize do
+        @auto_stop = true
+        @stop_all = true
+        wake
+      end
+    end
+
+    private
+
+    # Always wake when we add new work
+    def work(s) work << s; wake; end
+
+    def wake()
+      @wake[1].write_nonblock('x') rescue nil
+    end
+
+    def connection_driver(io, opts, server=false)
+      opts[:container_id] ||= @id
+      opts[:handler] ||= @handler
+      ConnectionDriver.new(self, io, opts, server)
+    end
+
+    def add(s)
+      @lock.synchronize do
+        @active += 1
+      end
+      @work << s
+      wake
+      return s
+    end
+
+    def rearm s
+      if s.send :finished?
+        @lock.synchronize { @work << nil if (@active -= 1).zero? && @auto_stop }
+      else
+        @lock.synchronize { @selectables << s }
+      end
+      wake
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/endpoint.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/endpoint.rb b/proton-c/bindings/ruby/lib/core/endpoint.rb
index 7c6f0a3..04551eb 100644
--- a/proton-c/bindings/ruby/lib/core/endpoint.rb
+++ b/proton-c/bindings/ruby/lib/core/endpoint.rb
@@ -69,6 +69,9 @@ module Qpid::Proton
       object_to_condition(@condition, self._local_condition)
     end
 
+    def condition
+      condition_to_object(_local_condition) || remote_condition; end
+
     # @private
     def remote_condition
       condition_to_object(self._remote_condition)
@@ -82,6 +85,10 @@ module Qpid::Proton
       self.connection.transport
     end
 
+    # @private
+    # @return [Bool] true if {#state} has all the bits of `mask` set
+    def check_state(mask) (self.state & mask) == mask; end
+
     # @return [Bool] true if endpoint has sent and received a CLOSE frame
     def closed?() check_state(LOCAL_CLOSED | REMOTE_CLOSED); end
 
@@ -137,10 +144,6 @@ module Qpid::Proton
       Cproton.pn_decref(impl)
     end
 
-    private
-
-    def check_state(mask) (self.state & mask) == mask; end
-
   end
 
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/listener.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/listener.rb b/proton-c/bindings/ruby/lib/core/listener.rb
new file mode 100644
index 0000000..e5f5c0d
--- /dev/null
+++ b/proton-c/bindings/ruby/lib/core/listener.rb
@@ -0,0 +1,110 @@
+#--
+# 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.
+#++
+
+module Qpid::Proton
+  # A listener for incoming connections.
+  #
+  # Create with {Container#listen} or  {Container#listen_with}
+  class Listener
+    # The listener's container
+    attr_reader :container
+
+    # Close the listener
+    # @param error [Condition] Optional error condition.
+    def close(error=nil)
+      @closed ||= Condition.make(error) || true
+      @io.close_read rescue nil # Cause listener to wake out of IO.select
+    end
+
+    # Get the {IO} server socket used by the listener
+    def to_io() @io; end
+
+    private                     # Called by {Container}
+
+    def initialize(io, handler)
+      @io, @handler = io, handler
+    end
+
+    def process
+      unless @closed
+        unless @open_dispatched
+          dispatch(:on_open)
+          @open_dispatched = true
+        end
+        begin
+          return @io.accept, dispatch(:on_accept)
+        rescue IO::WaitReadable, Errno::EINTR
+        rescue IOError, SystemCallError => e
+          close e
+        end
+      end
+      if @closed
+        dispatch(:on_error, @closed) if @closed != true
+        dispatch(:on_close)
+        close @io unless @io.closed? rescue nil
+      end
+    end
+
+    def can_read?() true; end
+    def can_write?() false; end
+    def finished?() @closed; end
+
+    # TODO aconway 2017-11-06: logging strategy
+    TRUE = Set[:true, :"1", :yes, :on]
+    def log?()
+      enabled = ENV['PN_TRACE_EVT']
+      TRUE.include? enabled.downcase.to_sym if enabled
+    end
+
+    def dispatch(method, *args)
+      STDERR.puts "(Listener 0x#{object_id.to_s(16)})[#{method}]" if log?
+      @handler.send(method, self, *args) if @handler && @handler.respond_to?(method)
+    end
+  end
+
+
+  # Class that handles listener events and provides options for accepted
+  # connections. This class simply returns a fixed set of options for every
+  # connection accepted, but you can subclass and override all of the on_
+  # methods to provide more interesting behaviour.
+  class ListenHandler
+    # @param opts [Hash] Options to return from on_accept.
+    def initialize(opts={}) @opts = opts; end
+
+    # Called when the listener is ready to accept connections.
+    # @param listener [Listener] The listener
+    def on_open(listener) end
+
+    # Called if an error occurs.
+    # If there is an error while opening the listener, this method is
+    # called and {#on_open} is not
+    # @param listener [Listener]
+    # @param what [Condition] Information about the error.
+    def on_error(listener, what) end
+
+    # Called when a listener accepts a new connection.
+    # @param listener [Listener] The listener
+    # @return [Hash] Options to apply to the incoming connection, see {#connect}
+    def on_accept(listener) @opts; end
+
+    # Called when the listener closes.
+    # @param listener [Listener] The listener accepting the connection.
+    def on_close(listener) end
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/session.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/session.rb b/proton-c/bindings/ruby/lib/core/session.rb
index 81135eb..3120d3f 100644
--- a/proton-c/bindings/ruby/lib/core/session.rb
+++ b/proton-c/bindings/ruby/lib/core/session.rb
@@ -126,7 +126,7 @@ module Qpid::Proton
     # @deprecated use {#open_receiver}
     def receiver(name) Receiver.new(Cproton.pn_receiver(@impl, name)); end
 
-    # TODO aconway 2016-01-04: doc options or target param
+    # TODO aconway 2016-01-04: doc options or target param, move option handling to Link.
     def open_receiver(options = {})
       options = { :source => options } if options.is_a? String
       receiver = Receiver.new Cproton.pn_receiver(@impl, options[:name] || connection.link_name)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/transport.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/transport.rb b/proton-c/bindings/ruby/lib/core/transport.rb
index 61767fd..8804c8a 100644
--- a/proton-c/bindings/ruby/lib/core/transport.rb
+++ b/proton-c/bindings/ruby/lib/core/transport.rb
@@ -77,11 +77,6 @@ module Qpid::Proton
     TRACE_DRV = Cproton::PN_TRACE_DRV
 
     # @private
-    CLIENT = 1
-    # @private
-    SERVER = 2
-
-    # @private
     include Util::SwigHelper
 
     # @private
@@ -213,26 +208,19 @@ module Qpid::Proton
     def self.wrap(impl)
       return nil if impl.nil?
 
-      self.fetch_instance(impl, :pn_transport_attachments) || Transport.new(nil, impl)
+      self.fetch_instance(impl, :pn_transport_attachments) || Transport.new(impl)
     end
 
     # Creates a new transport instance.
-    #
-    # @param mode [Integer] The transport mode, either CLIENT or SERVER
-    # @param impl [pn_transport_t] Should not be used.
-    #
-    # @raise [TransportError] If the mode is invalid.
-    #
-    def initialize(mode = nil, impl = Cproton.pn_transport)
+    def initialize(impl = Cproton.pn_transport)
       @impl = impl
-      if mode == SERVER
-        Cproton.pn_transport_set_server(@impl)
-      elsif (!mode.nil? && mode != CLIENT)
-        raise TransportError.new("cannot create transport for mode: #{mode}")
-      end
       self.class.store_instance(self, :pn_transport_attachments)
     end
 
+    # Set server mode for this tranport - enables protocol detection
+    # and server-side authentication for incoming connections
+    def set_server() Cproton.pn_transport_set_server(@impl); end
+
     # Returns whether the transport has any buffered data.
     #
     # @return [Boolean] True if the transport has no buffered data.
@@ -418,6 +406,12 @@ module Qpid::Proton
       !@ssl.nil?
     end
 
+    # @private
+    def apply opts
+      if opts[:sasl_enabled] != false # SASL is not disabled.
+        sasl.allow_insecure_mechs = opts[:sasl_allow_insecure_mechs] if opts[:sasl_allow_insecure_mechs]
+        sasl.allowed_mechs = opts[:sasl_allowed_mechs] if opts[:sasl_allowed_mechs]
+      end
+    end
   end
-
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/uri.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/uri.rb b/proton-c/bindings/ruby/lib/core/uri.rb
new file mode 100644
index 0000000..b29a719
--- /dev/null
+++ b/proton-c/bindings/ruby/lib/core/uri.rb
@@ -0,0 +1,50 @@
+#--
+# 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.
+#++
+
+require 'uri'
+
+module URI
+  # AMQP URI scheme for the AMQP protocol
+  class AMQP < Generic
+    DEFAULT_PORT = 5672
+  end
+  @@schemes['AMQP'] = AMQP
+
+  # AMQPS URI scheme for the AMQP protocol over TLS
+  class AMQPS < AMQP
+    DEFAULT_PORT = 5671
+  end
+  @@schemes['AMQPS'] = AMQPS
+end
+
+module Qpid::Proton
+  # Convert s to an {URI::AMQP} or {URI::AMQPS}
+  # @param s [String,URI] If s has no scheme, use the {URI::AMQP} scheme
+  # @return [URI::AMQP]
+  # @raise [BadURIError] If s has a scheme that is not "amqp" or "amqps"
+  def self.amqp_uri(s)
+    u = URI(s)
+    u.host ||= ""               # Behaves badly with nil host
+    return u if u.is_a? URI::AMQP
+    raise URI::BadURIError, "Not an AMQP URI: '#{u}'" if u.scheme
+    u.scheme = "amqp" unless u.scheme
+    u = URI::parse(u.to_s)      # Re-parse with amqp scheme
+    return u
+  end
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/core/url.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/core/url.rb b/proton-c/bindings/ruby/lib/core/url.rb
index 30d2d87..b54df66 100644
--- a/proton-c/bindings/ruby/lib/core/url.rb
+++ b/proton-c/bindings/ruby/lib/core/url.rb
@@ -23,6 +23,7 @@ module Qpid::Proton
 
     attr_reader :scheme
     attr_reader :username
+    alias :user :username
     attr_reader :password
     attr_reader :host
     attr_reader :port
@@ -60,10 +61,14 @@ module Qpid::Proton
       Cproton.pn_url_get_port(@url).to_i
     end
 
+    # @return [String] Convert to string
     def to_s
       "#{@scheme}://#{@username.nil? ? '' : @username}#{@password.nil? ? '' : '@' + @password + ':'}#{@host}:#{@port}/#{@path}"
     end
 
+    # @return [String] Allow implicit conversion by {String#try_convert}
+    alias :to_str :to_s
+
     private
 
     def defaults

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/event/event.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/event/event.rb b/proton-c/bindings/ruby/lib/event/event.rb
index e839f63..67d5e92 100644
--- a/proton-c/bindings/ruby/lib/event/event.rb
+++ b/proton-c/bindings/ruby/lib/event/event.rb
@@ -210,7 +210,7 @@ module Qpid::Proton
           Cproton.pn_handler_dispatch(handler.impl, @impl, type.number)
         else
           result = Qpid::Proton::Event.dispatch(handler, type.method, self)
-          if (result != "DELEGATED") && handler.respond_to?(:handlers)
+          if (result != "DELEGATED") && handler.respond_to?(:handlers) && handler.handlers
             handler.handlers.each do |hndlr|
               self.dispatch(hndlr)
             end
@@ -228,10 +228,11 @@ module Qpid::Proton
       end
 
       def container
-        impl = Cproton.pn_event_reactor(@impl)
-        Qpid::Proton::Util::ClassWrapper::WRAPPERS["pn_reactor"].call(impl)
+        @container || Util::ClassWrapper::WRAPPERS["pn_reactor"].call(Cproton.pn_event_reactor(@impl))
       end
 
+      def container=(c); @container = c; end
+
       # Returns the transport for this event.
       #
       # @return [Transport, nil] The transport.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/handler/endpoint_state_handler.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/handler/endpoint_state_handler.rb b/proton-c/bindings/ruby/lib/handler/endpoint_state_handler.rb
index 11e970a..8a1a16e 100644
--- a/proton-c/bindings/ruby/lib/handler/endpoint_state_handler.rb
+++ b/proton-c/bindings/ruby/lib/handler/endpoint_state_handler.rb
@@ -84,11 +84,11 @@ module Qpid::Proton::Handler
     end
 
     def on_connection_remote_open(event)
-      if !(event.connection.state & Qpid::Proton::Endpoint::LOCAL_ACTIVE).zero?
+      if event.connection.local_active?
         self.on_connection_opened(event)
       elsif event.connection.local_uninit?
         self.on_connection_opening(event)
-        event.connection.open
+        event.connection.open unless event.connection.local_active?
       end
     end
 
@@ -110,7 +110,7 @@ module Qpid::Proton::Handler
     end
 
     def on_link_remote_open(event)
-      if !(event.link.state & Qpid::Proton::Endpoint::LOCAL_ACTIVE).zero?
+      if event.link.local_active?
         self.on_link_opened(event)
       elsif event.link.local_uninit?
         self.on_link_opening(event)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/qpid_proton.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/qpid_proton.rb b/proton-c/bindings/ruby/lib/qpid_proton.rb
index dae9b5f..43b8071 100644
--- a/proton-c/bindings/ruby/lib/qpid_proton.rb
+++ b/proton-c/bindings/ruby/lib/qpid_proton.rb
@@ -27,6 +27,9 @@ else
   require "securerandom"
 end
 
+DEPRECATION = "[DEPRECATION]"
+def deprecated(old, new) warn "#{DEPRECATION} #{old} is deprecated, use #{new}"; end
+
 # Exception classes
 require "core/exceptions"
 
@@ -61,6 +64,7 @@ require "event/collector"
 
 # Main Proton classes
 require "core/selectable"
+require "core/uri"
 require "core/message"
 require "core/endpoint"
 require "core/session"
@@ -109,6 +113,10 @@ require "reactor/session_per_connection"
 require "reactor/container"
 require "reactor/link_option"
 
+# Core classes that depend on handlers and events
+require "core/container"
+require "core/connection_driver"
+
 module Qpid::Proton
   # @private
   def self.registry

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/reactor/reactor.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/reactor/reactor.rb b/proton-c/bindings/ruby/lib/reactor/reactor.rb
index a84a716..88d062e 100644
--- a/proton-c/bindings/ruby/lib/reactor/reactor.rb
+++ b/proton-c/bindings/ruby/lib/reactor/reactor.rb
@@ -51,6 +51,7 @@ module Qpid::Proton::Reactor
     end
 
     def initialize(handlers, options = {})
+      deprecated(self.class, "Qpid::Proton::Container")
       @impl = options[:impl]
       if @impl.nil?
         @impl = Cproton.pn_reactor

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/util/condition.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/util/condition.rb b/proton-c/bindings/ruby/lib/util/condition.rb
index ad49595..37c41a0 100644
--- a/proton-c/bindings/ruby/lib/util/condition.rb
+++ b/proton-c/bindings/ruby/lib/util/condition.rb
@@ -17,7 +17,7 @@
 # under the License.
 #++
 
-module Qpid::Proton::Util
+module Qpid::Proton
 
   class Condition
 
@@ -29,19 +29,40 @@ module Qpid::Proton::Util
       @info = info
     end
 
-    # @private
-    def to_s
-      "Condition(#{@name}, #{@description}, #{@info})"
-    end
+    def to_s() "#{@name}: #{@description
+    def to_s() "#{@name}: #{@description}"; end
+}"; end
+
+    def inspect() "#{self.class.name}(#{@name.inspect}, #{@description.inspect}, #{@info.inspect})"; end
 
-    # @private
     def ==(other)
-      ((other.class = self.class) &&
+      ((other.is_a? Condition) &&
        (other.name == self.name) && 
        (other.description == self.description) &&
        (other.info == self.info))
     end
 
+    # Make a condition.
+    # @param obj the object to turn into a condition
+    # @param default_name condition name to use if obj does not imply a name
+    # @return
+    # - when Condition return obj unchanged
+    # - when Exception return Condition(obj.class.name, obj.to_s)
+    # - when nil then nil
+    # - else return Condition(default_name, obj.to_s)
+    # If objey
+    def self.make(obj, default_name="proton")
+      case obj
+      when Condition then obj
+      when Exception then Condition.new(obj.class.name, obj.to_s)
+      when nil then nil
+      else Condition.new(default_name, obj.to_s)
+      end
+    end
+
   end
 
+  module Util                   #TODO aconway 2017-10-28: backwards compat
+    Condition = Qpid::Proton::Condition
+  end
 end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/util/engine.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/util/engine.rb b/proton-c/bindings/ruby/lib/util/engine.rb
index e34faaa..fa5c038 100644
--- a/proton-c/bindings/ruby/lib/util/engine.rb
+++ b/proton-c/bindings/ruby/lib/util/engine.rb
@@ -70,8 +70,8 @@ module Qpid::Proton::Util
       unless object.nil?
         Cproton.pn_condition_set_name(condition, object.name)
         Cproton.pn_condition_set_description(condition, object.description)
-        info = Data.new(Cproton.pn_condition_info(condition))
-        if object.info?
+        if !object.info.nil?
+          info = Data.new(Cproton.pn_condition_info(condition))
           info.object = object.info
         end
       end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/lib/util/uri.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/util/uri.rb b/proton-c/bindings/ruby/lib/util/uri.rb
deleted file mode 100644
index 0820746..0000000
--- a/proton-c/bindings/ruby/lib/util/uri.rb
+++ /dev/null
@@ -1,27 +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.
-#++
-
-require 'uri'
-
-module URI
-  class AMQP < Generic; DEFAULT_PORT = 5672; end
-  @@schemes['AMQP'] = AMQP
-  class AMQPS < Generic; DEFAULT_PORT = 5671; end
-  @@schemes['AMQPS'] = AMQPS
-end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/tests/test_connection_driver.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_connection_driver.rb b/proton-c/bindings/ruby/tests/test_connection_driver.rb
index 2ddc8ef..174e86d 100644
--- a/proton-c/bindings/ruby/tests/test_connection_driver.rb
+++ b/proton-c/bindings/ruby/tests/test_connection_driver.rb
@@ -39,11 +39,11 @@ class ConnectionDriverTest < Minitest::Test
       def on_message(event) @message = event.message; event.connection.close; end
     end
 
-    sender = ConnectionDriver.new(@sockets[0], send_class.new)
+    sender = ConnectionDriver.new(@sockets[0], {:handler => send_class.new})
     sender.connection.open();
     sender.connection.open_sender()
 
-    receiver = ConnectionDriver.new(@sockets[1], recv_class.new)
+    receiver = ConnectionDriver.new(@sockets[1], {:handler => recv_class.new})
     drivers = [sender, receiver]
     until drivers.all? { |d| d.finished? }
       rd = drivers.select {|d| d.can_read? }
@@ -60,7 +60,7 @@ class ConnectionDriverTest < Minitest::Test
     idle_class = Class.new(MessagingHandler) do
       def on_connection_bound(event) event.transport.idle_timeout = 10; end
     end
-    drivers = [ConnectionDriver.new(@sockets[0], idle_class.new), ConnectionDriver.new(@sockets[1])]
+    drivers = [ConnectionDriver.new(@sockets[0], {:handler => idle_class.new}), ConnectionDriver.new(@sockets[1])]
     drivers[0].connection.open()
     now = Time.now
     drivers.each { |d| d.process(true, true, now) } until drivers[0].connection.open?

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/tests/test_container.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_container.rb b/proton-c/bindings/ruby/tests/test_container.rb
index 9c1d46c..41fa133 100644
--- a/proton-c/bindings/ruby/tests/test_container.rb
+++ b/proton-c/bindings/ruby/tests/test_container.rb
@@ -19,63 +19,137 @@
 
 require 'test_tools'
 require 'minitest/unit'
+require 'socket'
 
 Message = Qpid::Proton::Message
 SASL = Qpid::Proton::SASL
-URL = Qpid::Proton::URL
+Disposition = Qpid::Proton::Disposition
 
-class ContainerTest < Minitest::Test
+# Container that listens on a random port
+class TestContainer < Container
 
-  def test_simple()
+  def initialize(opts = {}, lopts = {})
+    super opts
+    @server = TCPServer.open(0)
+    @listener = listen_io(@server, ListenOnceHandler.new(lopts))
+  end
 
-    hc = Class.new(TestServer) do
-      attr_reader :accepted
+  def port() @server.addr[1]; end
+  def url() "amqp://:#{port}"; end
+end
 
-      def on_start(event)
-        super
-        event.container.create_sender("amqp://#{addr}", {:name => "testlink"})
+class ContainerTest < Minitest::Test
+
+  def test_simple()
+    sh = Class.new(MessagingHandler) do
+      attr_reader :accepted, :sent
+      def on_sendable(e)
+        e.link.send Message.new("foo") unless @sent
+        @sent = true
       end
 
-      def on_sendable(event)
-        if @sent.nil? && event.sender.credit > 0
-          event.sender.send(Message.new("testmessage"))
-          @sent = true
-        end
+      def on_accepted(e)
+        @accepted = true
+        e.connection.close
+      end
+    end.new
+
+    rh = Class.new(MessagingHandler) do
+      attr_reader :message, :link
+      def on_link_opening(e)
+        @link = e.link
+        e.link.open
+        e.link.flow(1)
       end
 
-      def on_accepted(event)
-        @accepted = event
-        event.container.stop
+      def on_message(e)
+        @message = e.message;
+        e.delivery.update Disposition::ACCEPTED
+        e.delivery.settle
       end
-    end
-    h = hc.new
-    Container.new(h).run
-    assert_instance_of(Qpid::Proton::Event::Event, h.accepted)
-    assert_equal "testlink", h.links.first.name
-    assert_equal "testmessage", h.messages.first.body
+    end.new
+
+    c = TestContainer.new({:id => __method__.to_s, :handler => rh})
+    c.connect(c.url, {:handler => sh}).open_sender({:name => "testlink"})
+    c.run
+
+    assert sh.accepted
+    assert_equal "testlink", rh.link.name
+    assert_equal "foo", rh.message.body
+    assert_equal "test_simple", rh.link.connection.container_id
   end
+
+  class CloseOnOpenHandler < TestHandler
+    def on_connection_opened(e) e.connection.close; end
+    def on_connection_closing(e) e.connection.close; end
+  end
+
+  def test_auto_stop
+    c1 = Container.new "#{__method__}1"
+    c2 = Container.new "#{__method__}2"
+
+    # A listener and a connection
+    t1 = 3.times.collect { Thread.new { c1.run } }
+    l = c1.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => CloseOnOpenHandler.new}))
+    c1.connect("amqp://:#{l.to_io.addr[1]}", { :handler => CloseOnOpenHandler.new} )
+    t1.each { |t| assert t.join(1) }
+
+    # Connect between different containers, c2 has only a connection
+    t1 = Thread.new { c1.run }
+    l = c1.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => CloseOnOpenHandler.new}))
+    t2 = Thread.new {c2.run }
+    c2.connect("amqp://:#{l.to_io.addr[1]}", { :handler => CloseOnOpenHandler.new} )
+    assert t2.join(1)
+    assert t1.join(1)
+  end
+
+  def test_auto_stop_listener_only
+    c1 = Container.new "#{__method__}1"
+    # Listener only, external close
+    t1 = Thread.new { c1.run }
+    l = c1.listen_io(TCPServer.new(0))
+    l.close
+    assert t1.join(1)
+  end
+
+  def test_stop
+    c = Container.new __method__
+    c.auto_stop = false
+    l = c.listen_io(TCPServer.new(0))
+    c.connect("amqp://:#{l.to_io.addr[1]}")
+    threads = 5.times.collect { Thread.new { c.run } }
+    assert_nil threads[0].join(0.001)
+    c.stop
+    threads.each { |t| assert t.join(1) }
+    assert c.auto_stop          # Set by stop
+
+    # Stop an empty container
+    threads = 5.times.collect { Thread.new { c.run } }
+    assert_nil threads[0].join(0.001)
+    c.stop
+    threads.each { |t| assert t.join(1) }
+  end
+
 end
 
+
 class ContainerSASLTest < Minitest::Test
 
   # Handler for test client/server that sets up server and client SASL options
-  class SASLHandler < TestServer
+  class SASLHandler < TestHandler
 
-    attr_accessor :url
-
-    def initialize(opts={}, mechanisms=nil, insecure=nil, realm=nil)
+    def initialize(url="amqp://", opts={}, mechanisms=nil, insecure=nil, realm=nil)
       super()
-      @opts, @mechanisms, @insecure, @realm = opts, mechanisms, insecure, realm
+      @url, @opts, @mechanisms, @insecure, @realm = url, opts, mechanisms, insecure, realm
     end
 
     def on_start(e)
       super
-      @client = e.container.connect(@url || "amqp://#{addr}", @opts)
+      @client = e.container.connect("#{@url}:#{e.container.port}", @opts)
     end
 
     def on_connection_bound(e)
       if e.connection != @client # Incoming server connection
-        @listener.close
         sasl = e.transport.sasl
         sasl.allow_insecure_mechs = @insecure unless @insecure.nil?
         sasl.allowed_mechs = @mechanisms unless @mechanisms.nil?
@@ -86,6 +160,7 @@ class ContainerSASLTest < Minitest::Test
     end
 
     attr_reader :auth_user
+
     def on_connection_opened(e)
       super
       if e.connection == @client
@@ -135,26 +210,28 @@ mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
   end
 
   def test_sasl_anonymous()
-    s = SASLHandler.new({:sasl_allowed_mechs => "ANONYMOUS"}, "ANONYMOUS")
-    Container.new(s).run
+    s = SASLHandler.new("amqp://",  {:sasl_allowed_mechs => "ANONYMOUS"})
+    TestContainer.new({:id => __method__.to_s, :handler => s}, {:sasl_allowed_mechs => "ANONYMOUS"}).run
     assert_nil(s.connections[0].user)
   end
 
   def test_sasl_plain_url()
+    skip unless SASL.extended?
     # Use default realm with URL, should authenticate with "default_password"
-    s = SASLHandler.new({:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}, "PLAIN", true)
-    s.url = ("amqp://user:default_password@#{s.addr}")
-    Container.new(s).run
+    opts = {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}
+    s = SASLHandler.new("amqp://user:default_password@",  opts)
+    TestContainer.new({:id => __method__.to_s, :handler => s}, opts).run
     assert_equal(2, s.connections.size)
     assert_equal("user", s.auth_user)
   end
 
   def test_sasl_plain_options()
+    skip unless SASL.extended?
     # Use default realm with connection options, should authenticate with "default_password"
     opts = {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true,
             :user => 'user', :password => 'default_password' }
-    s = SASLHandler.new(opts, "PLAIN", true)
-    Container.new(s).run
+    s = SASLHandler.new("amqp://", opts)
+    TestContainer.new({:id => __method__.to_s, :handler => s}, {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true}).run
     assert_equal(2, s.connections.size)
     assert_equal("user", s.auth_user)
   end
@@ -162,9 +239,9 @@ mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
   # Ensure we don't allow PLAIN if allow_insecure_mechs = true is not explicitly set
   def test_disallow_insecure()
     # Don't set allow_insecure_mechs, but try to use PLAIN
-    s = SASLHandler.new({:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}, "PLAIN")
-    s.url = "amqp://user:password@#{s.addr}"
-    e = assert_raises(TestError) { Container.new(s).run }
+    s = SASLHandler.new("amqp://user:password@", {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true})
+    e = assert_raises(TestError) { TestContainer.new({:id => __method__.to_s, :handler => s}, {:sasl_allowed_mechs => "PLAIN"}).run }
     assert_match(/PN_TRANSPORT_ERROR.*unauthorized-access/, e.to_s)
   end
 end
+


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


[03/10] qpid-proton git commit: PROTON-1064: [ruby] use separate directory for gem build

Posted by ac...@apache.org.
PROTON-1064: [ruby] use separate directory for gem build

Copying ruby sources into the root binary directory for the gem build creates
confusion since this directory is on the RUBYLIB path for generated SWIG code.


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

Branch: refs/heads/master
Commit: bca7fd17a4d4156a5b8a7c124cae6a595114b75e
Parents: 82681ad
Author: Alan Conway <ac...@redhat.com>
Authored: Mon Nov 6 10:30:27 2017 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/ruby/CMakeLists.txt | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/bca7fd17/proton-c/bindings/ruby/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/CMakeLists.txt b/proton-c/bindings/ruby/CMakeLists.txt
index 5ceb7f0..93364e2 100644
--- a/proton-c/bindings/ruby/CMakeLists.txt
+++ b/proton-c/bindings/ruby/CMakeLists.txt
@@ -51,14 +51,16 @@ find_program(GEM_EXE gem DOC "Program to build and install ruby gem packages")
 mark_as_advanced(GEM_EXE)
 if (GEM_EXE)
   # Copy source and generated files to the build tree so we can build the gem in one place
-  configure_file(${src}/qpid_proton.gemspec.in ${bin}/qpid_proton.gemspec)
+  configure_file(${src}/qpid_proton.gemspec.in ${bin}/gem/qpid_proton.gemspec)
   add_custom_command(
     OUTPUT ${bin}/qpid_proton-${PN_VERSION}.gem
-    COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${bin}
-    COMMAND ${CMAKE_COMMAND} -E copy ${bin}/rubyRUBY_wrap.c ${bin}/ext/cproton/cproton.c
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${src} ${bin}/gem
+    COMMAND ${CMAKE_COMMAND} -E copy ${bin}/rubyRUBY_wrap.c ${bin}/gem/ext/cproton/cproton.c
     COMMAND ${GEM_EXE} build qpid_proton.gemspec
-    DEPENDS ${RUBY_SRC} ${src}/LICENSE ${src}/TODO ${src}/ChangeLog cproton-ruby
-    WORKING_DIRECTORY ${bin})
+    COMMAND ${CMAKE_COMMAND} -E copy ${bin}/gem/qpid_proton-${PN_VERSION}.gem ${bin}
+    WORKING_DIRECTORY ${bin}/gem
+    DEPENDS ${RUBY_SRC} ${src}/LICENSE ${src}/TODO ${src}/ChangeLog cproton-ruby ${bin}/rubyRUBY_wrap.c
+    )
 
   add_custom_target(ruby-gem ALL DEPENDS ${bin}/qpid_proton-${PN_VERSION}.gem )
 endif ()


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


[08/10] qpid-proton git commit: PROTON-1064: [ruby] New Container with native ruby IO

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/51cda9d5/proton-c/bindings/ruby/tests/test_tools.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_tools.rb b/proton-c/bindings/ruby/tests/test_tools.rb
index 8de86e2..b45add2 100644
--- a/proton-c/bindings/ruby/tests/test_tools.rb
+++ b/proton-c/bindings/ruby/tests/test_tools.rb
@@ -24,7 +24,8 @@ require 'qpid_proton'
 require 'thread'
 require 'socket'
 
-Container = Qpid::Proton::Reactor::Container
+Container = Qpid::Proton::Container
+ListenHandler = Qpid::Proton::Listener
 MessagingHandler = Qpid::Proton::Handler::MessagingHandler
 
 class TestError < Exception; end
@@ -44,6 +45,7 @@ end
 
 # Handler that records some common events that are checked by tests
 class TestHandler < MessagingHandler
+  # TODO aconway 2017-10-28: make on_error stuff part of the default handler.
 
   attr_reader :errors, :connections, :sessions, :links, :messages
 
@@ -67,7 +69,7 @@ class TestHandler < MessagingHandler
 
   # TODO aconway 2017-08-15: implement in MessagingHandler
   def on_error(event, endpoint)
-    @errors.push "#{event.type}: #{endpoint.condition.name}: #{endpoint.condition.description}"
+    @errors.push "#{event.type}: #{endpoint.condition.inspect}"
     raise_errors if @raise_errors
   end
 
@@ -76,7 +78,7 @@ class TestHandler < MessagingHandler
   end
 
   def on_connection_error(event)
-    on_error(event, event.condition)
+    on_error(event, event.connection)
   end
 
   def on_session_error(event)
@@ -87,21 +89,20 @@ class TestHandler < MessagingHandler
     on_error(event, event.link)
   end
 
-  def on_opened(queue, endpoint)
+  def endpoint_opened(queue, endpoint)
     queue.push(endpoint)
-    endpoint.open
   end
 
   def on_connection_opened(event)
-    on_opened(@connections, event.connection)
+    endpoint_opened(@connections, event.connection)
   end
 
   def on_session_opened(event)
-    on_opened(@sessions, event.session)
+    endpoint_opened(@sessions, event.session)
   end
 
   def on_link_opened(event)
-    on_opened(@links, event.link)
+    endpoint_opened(@links, event.link)
   end
 
   def on_message(event)
@@ -109,19 +110,10 @@ class TestHandler < MessagingHandler
   end
 end
 
-# A TestHandler that listens on a random port
-class TestServer < TestHandler
-  def initialize
-    super
-    @server = TCPServer.open(0)
-  end
-
-  def host() ""; end
-  def port() @server.addr[1]; end
-  def addr() "#{host}:#{port}"; end
-
-  def on_start(e)
-    super
-    @listener = e.container.listen_io(@server)
-  end
+# ListenHandler that closes the Listener after first accept
+class ListenOnceHandler < ListenHandler
+  def initialize(opts={}) @opts=opts; end
+  def on_error(l, e)  raise TestError, e; end
+  def on_accept(l) l.close; return @opts; end
+  attr_reader :opts
 end


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


[02/10] qpid-proton git commit: PROTON-1064: [ruby] extend standard ruby URI with amqp/amqps schemes

Posted by ac...@apache.org.
PROTON-1064: [ruby] extend standard ruby URI with amqp/amqps schemes

Simple URI.Generic schemes with the correct default ports, plus
a helper function amqp_uri() to default a URI with no scheme to AMQP.


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

Branch: refs/heads/master
Commit: 8155c5af3c6f63a585354034089a72d7dac13895
Parents: 9bb1baa
Author: Alan Conway <ac...@redhat.com>
Authored: Sat Oct 28 17:24:35 2017 +0100
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 7 13:31:51 2017 -0500

----------------------------------------------------------------------
 proton-c/bindings/ruby/lib/util/uri.rb   | 27 ++++++++++++++++++
 proton-c/bindings/ruby/tests/test_uri.rb | 40 +++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8155c5af/proton-c/bindings/ruby/lib/util/uri.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/lib/util/uri.rb b/proton-c/bindings/ruby/lib/util/uri.rb
new file mode 100644
index 0000000..0820746
--- /dev/null
+++ b/proton-c/bindings/ruby/lib/util/uri.rb
@@ -0,0 +1,27 @@
+#--
+# 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.
+#++
+
+require 'uri'
+
+module URI
+  class AMQP < Generic; DEFAULT_PORT = 5672; end
+  @@schemes['AMQP'] = AMQP
+  class AMQPS < Generic; DEFAULT_PORT = 5671; end
+  @@schemes['AMQPS'] = AMQPS
+end

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8155c5af/proton-c/bindings/ruby/tests/test_uri.rb
----------------------------------------------------------------------
diff --git a/proton-c/bindings/ruby/tests/test_uri.rb b/proton-c/bindings/ruby/tests/test_uri.rb
new file mode 100644
index 0000000..e5279d8
--- /dev/null
+++ b/proton-c/bindings/ruby/tests/test_uri.rb
@@ -0,0 +1,40 @@
+#--
+# 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.
+#++
+
+require 'minitest/autorun'
+require 'qpid_proton'
+
+class TestURI < Minitest::Test
+
+  def amqp_uri(u) Qpid::Proton::amqp_uri(u); end
+
+  def test_amqp_uri
+    assert_equal URI("amqp:").port, 5672
+    assert_equal URI("amqps:").port, 5671
+    assert_equal URI("amqp://user:pass@host:1234/path"), amqp_uri("//user:pass@host:1234/path")
+    assert_equal URI("amqp://user:pass@host:1234/path"), amqp_uri("amqp://user:pass@host:1234/path")
+    assert_equal URI("amqps://user:pass@host:1234/path"), amqp_uri("amqps://user:pass@host:1234/path")
+    assert_equal URI("amqp://host:1234/path"), amqp_uri("//host:1234/path")
+    assert_equal URI("amqp://host:1234"), amqp_uri("//host:1234")
+    assert_equal URI("amqp://host"), amqp_uri("//host")
+    assert_equal URI("amqp://:1234"), amqp_uri("//:1234")
+    assert_raises(URI::BadURIError) { amqp_uri("http://foo") }
+  end
+
+end


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