PROTON-799: Rearranged Ruby library. Moved files to match more closely with the new code layout for the project.
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/46b91f78 Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/46b91f78 Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/46b91f78 Branch: refs/heads/ruby-engine-apis Commit: 46b91f7870bfc05273666ebe57c4b64d78ee8135 Parents: 995e00d Author: Darryl L. Pierce <mcpie...@gmail.com> Authored: Fri Feb 20 09:31:03 2015 -0500 Committer: Darryl L. Pierce <mcpie...@gmail.com> Committed: Thu May 14 09:28:11 2015 -0400 ---------------------------------------------------------------------- proton-c/bindings/ruby/lib/codec/data.rb | 788 +++++++++++++++++++ proton-c/bindings/ruby/lib/codec/mapping.rb | 170 ++++ proton-c/bindings/ruby/lib/core/exceptions.rb | 85 ++ proton-c/bindings/ruby/lib/core/message.rb | 621 +++++++++++++++ proton-c/bindings/ruby/lib/messenger/filters.rb | 67 ++ .../bindings/ruby/lib/messenger/messenger.rb | 702 +++++++++++++++++ .../bindings/ruby/lib/messenger/selectable.rb | 126 +++ .../bindings/ruby/lib/messenger/subscription.rb | 41 + proton-c/bindings/ruby/lib/messenger/tracker.rb | 42 + .../ruby/lib/messenger/tracker_status.rb | 73 ++ proton-c/bindings/ruby/lib/qpid_proton.rb | 43 +- proton-c/bindings/ruby/lib/qpid_proton/array.rb | 173 ---- proton-c/bindings/ruby/lib/qpid_proton/data.rb | 788 ------------------- .../bindings/ruby/lib/qpid_proton/described.rb | 66 -- .../ruby/lib/qpid_proton/exception_handling.rb | 127 --- .../bindings/ruby/lib/qpid_proton/exceptions.rb | 85 -- .../bindings/ruby/lib/qpid_proton/filters.rb | 67 -- proton-c/bindings/ruby/lib/qpid_proton/hash.rb | 86 -- .../bindings/ruby/lib/qpid_proton/mapping.rb | 170 ---- .../bindings/ruby/lib/qpid_proton/message.rb | 621 --------------- .../bindings/ruby/lib/qpid_proton/messenger.rb | 702 ----------------- .../bindings/ruby/lib/qpid_proton/selectable.rb | 126 --- .../bindings/ruby/lib/qpid_proton/strings.rb | 65 -- .../ruby/lib/qpid_proton/subscription.rb | 41 - .../bindings/ruby/lib/qpid_proton/tracker.rb | 42 - .../ruby/lib/qpid_proton/tracker_status.rb | 73 -- .../bindings/ruby/lib/qpid_proton/version.rb | 32 - proton-c/bindings/ruby/lib/types/array.rb | 173 ++++ proton-c/bindings/ruby/lib/types/described.rb | 66 ++ proton-c/bindings/ruby/lib/types/hash.rb | 86 ++ proton-c/bindings/ruby/lib/types/strings.rb | 65 ++ .../bindings/ruby/lib/util/error_handler.rb | 127 +++ proton-c/bindings/ruby/lib/util/version.rb | 32 + 33 files changed, 3291 insertions(+), 3280 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/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 new file mode 100644 index 0000000..b6b3002 --- /dev/null +++ b/proton-c/bindings/ruby/lib/codec/data.rb @@ -0,0 +1,788 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + # +DataError+ is raised when an error occurs while encoding + # or decoding data. + class DataError < Exception; end + + # The +Data+ class provides an interface for decoding, extracting, + # creating, and encoding arbitrary AMQP data. A +Data+ object + # contains a tree of AMQP values. Leaf nodes in this tree correspond + # to scalars in the AMQP type system such as INT or STRING. Interior + # nodes in this tree correspond to compound values in the AMQP type + # system such as *LIST*,*MAP*, *ARRAY*, or *DESCRIBED*. The root node + # of the tree is the +Data+ object itself and can have an arbitrary + # number of children. + # + # A +Data+ object maintains the notion of the current sibling node + # and a current parent node. Siblings are ordered within their parent. + # Values are accessed and/or added by using the #next, #prev, + # #enter, and #exit methods to navigate to the desired location in + # the tree and using the supplied variety of mutator and accessor + # methods to access or add a value of the desired type. + # + # The mutator methods will always add a value _after_ the current node + # in the tree. If the current node has a next sibling the mutator method + # will overwrite the value on this node. If there is no current node + # or the current node has no next sibling then one will be added. The + # accessor methods always set the added/modified node to the current + # node. The accessor methods read the value of the current node and do + # not change which node is current. + # + # The following types of scalar values are supported: + # + # * *NULL* + # * *BOOL* + # * *UBYTE* + # * *BYTE* + # * *USHORT* + # * *SHORT* + # * *UINT* + # * *INT* + # * *CHAR* + # * *ULONG* + # * *LONG* + # * *TIMESTAMP* + # * *FLOAT* + # * *DOUBLE* + # * *DECIMAL32* + # * *DECIMAL64* + # * *DECIMAL128* + # * *UUID* + # * *BINARY* + # * *STRING* + # * *SYMBOL* + # + # The following types of compound values are supported: + # + # * *DESCRIBED* + # * *ARRAY* + # * *LIST* + # * *MAP* + # + class Data + + # Creates a new instance with the specified capacity. + # + # ==== Options + # + # * capacity - the capacity + # + def initialize(capacity = 16) + if (!capacity.nil?) && + (capacity.is_a?(Fixnum) || + capacity.is_a?(Bignum)) + @data = Cproton.pn_data(capacity) + @free = true + else + @data = capacity + @free = false + end + + # destructor + ObjectSpace.define_finalizer(self, self.class.finalize!(@data)) + end + + def self.finalize!(data) # :nodoc: + proc { + Cproton.pn_data_free(data) if @free + } + end + + def to_s + tmp = Cproton.pn_string("") + Cproton.pn_inspect(@data, tmp) + result = Cproton.pn_string_get(tmp) + Cproton.pn_free(tmp) + return result + end + + # Clears the object. + def clear + Cproton.pn_data_clear(@data) + end + + # Clears the current node and sets the parent to the root node. + # + # Clearing the current node sets it _before_ the first node, calling + # #next will advance to the first node. + def rewind + Cproton.pn_data_rewind(@data) + end + + # Advances the current node to its next sibling and returns its types. + # + # If there is no next sibling the current node remains unchanged + # and nil is returned. + def next(print = false) + Cproton.pn_data_next(@data) + end + + # Advances the current node to its previous sibling and returns its type. + # + # If there is no previous sibling then the current node remains unchanged + # and nil is return. + def prev + return Cproton.pn_data_prev(@data) ? type : nil + end + + # Sets the parent node to the current node and clears the current node. + # + # Clearing the current node sets it _before_ the first child. + def enter + Cproton.pn_data_enter(@data) + end + + # Sets the current node to the parent node and the parent node to its own + # parent. + def exit + Cproton.pn_data_exit(@data) + end + + # Returns the numeric type code of the current node. + def type_code + dtype = Cproton.pn_data_type(@data) + return (dtype == -1) ? nil : dtype + end + + # Return the Type object for the current node + def type + Mapping.for_code(type_code) + end + + # Returns a representation of the data encoded in AMQP format. + def encode + buffer = "\0"*1024 + loop do + cd = Cproton.pn_data_encode(@data, buffer, buffer.length) + if cd == Cproton::PN_OVERFLOW + buffer *= 2 + elsif cd >= 0 + return buffer[0...cd] + else + check(cd) + end + end + end + + # Decodes the first value from supplied AMQP data and returns the number + # of bytes consumed. + # + # ==== Options + # + # * encoded - the encoded data + # + def decode(encoded) + check(Cproton.pn_data_decode(@data, encoded, encoded.length)) + end + + # Puts a list value. + # + # Elements may be filled by entering the list node and putting element + # values. + # + # ==== Examples + # + # data = Qpid::Proton::Data.new + # data.put_list + # data.enter + # data.int = 1 + # data.int = 2 + # data.int = 3 + # data.exit + # + def put_list + check(Cproton.pn_data_put_list(@data)) + end + + # If the current node is a list, this returns the number of elements. + # Otherwise, it returns zero. + # + # List elements can be accessed by entering the list. + # + # ==== Examples + # + # count = @data.list + # @data.enter + # (0...count).each + # type = @data.next + # puts "Value: #{@data.string}" if type == STRING + # # ... process other node types + # end + def list + Cproton.pn_data_get_list(@data) + end + + # Puts a map value. + # + # Elements may be filled by entering the map node and putting alternating + # key/value pairs. + # + # ==== Examples + # + # data = Qpid::Proton::Data.new + # data.put_map + # data.enter + # data.string = "key" + # data.string = "value" + # data.exit + # + def put_map + check(Cproton.pn_data_put_map(@data)) + end + + # If the current node is a map, this returns the number of child + # elements. Otherwise, it returns zero. + # + # Key/value pairs can be accessed by entering the map. + # + # ==== Examples + # + # count = @data.map + # @data.enter + # (0...count).each do + # type = @data.next + # puts "Key=#{@data.string}" if type == STRING + # # ... process other key types + # type = @data.next + # puts "Value=#{@data.string}" if type == STRING + # # ... process other value types + # end + # @data.exit + def map + Cproton.pn_data_get_map(@data) + end + + def get_map # :nodoc: + ::Hash.proton_data_get(self) + end + + # Puts an array value. + # + # Elements may be filled by entering the array node and putting the + # element values. The values must all be of the specified array element + # type. + # + # If an array is *described* then the first child value of the array + # is the descriptor and may be of any type. + # + # ==== Options + # + # * described - specifies whether the array is described + # * element_type - the type of the array elements + # + # ==== Examples + # + # # create an array of integer values + # data = Qpid::Proton::Data.new + # data.put_array(false, INT) + # data.enter + # data.int = 1 + # data.int = 2 + # data.int = 3 + # data.exit + # + # # create an array of double values + # data.put_array(true, DOUBLE) + # data.enter + # data.symbol = "array-descriptor" + # data.double = 1.1 + # data.double = 1.2 + # data.double = 1.3 + # data.exit + # + def put_array(described, element_type) + check(Cproton.pn_data_put_array(@data, described, element_type.code)) + end + + # If the current node is an array, returns a tuple of the element count, a + # boolean indicating whether the array is described, and the type of each + # element. Otherwise it returns +(0, false, nil). + # + # Array data can be accessed by entering the array. + # + # ==== Examples + # + # # get the details of thecurrent array + # count, described, array_type = @data.array + # + # # enter the node + # data.enter + # + # # get the next node + # data.next + # puts "Descriptor: #{data.symbol}" if described + # (0...count).each do + # @data.next + # puts "Element: #{@data.string}" + # end + def array + count = Cproton.pn_data_get_array(@data) + described = Cproton.pn_data_is_array_described(@data) + array_type = Cproton.pn_data_get_array_type(@data) + return nil if array_type == -1 + [count, described, Mapping.for_code(array_type) ] + end + + def get_array # :nodoc: + ::Array.proton_get(self) + end + + # Puts a described value. + # + # A described node has two children, the descriptor and the value. + # These are specified by entering the node and putting the + # desired values. + # + # ==== Examples + # + # data = Qpid::Proton::Data.new + # data.put_described + # data.enter + # data.symbol = "value-descriptor" + # data.string = "the value" + # data.exit + # + def put_described + check(Cproton.pn_data_put_described(@data)) + end + + def get_described # :nodoc: + raise TypeError, "not a described type" unless self.described? + self.enter + self.next + type = self.type + descriptor = type.get(self) + self.next + type = self.type + value = type.get(self) + self.exit + Described.new(descriptor, value) + end + + # Checks if the current node is a described value. + # + # The described and value may be accessed by entering the described value. + # + # ==== Examples + # + # if @data.described? + # @data.enter + # puts "The symbol is #{@data.symbol}" + # puts "The value is #{@data.string}" + # end + def described? + Cproton.pn_data_is_described(@data) + end + + # Puts a null value. + def null + check(Cproton.pn_data_put_null(@data)) + end + + # Utility method for Qpid::Proton::Mapping + def null=(value) # :nodoc: + null + end + + # Checks if the current node is null. + def null? + Cproton.pn_data_is_null(@data) + end + + # Puts a boolean value. + # + # ==== Options + # + # * value - the boolean value + def bool=(value) + check(Cproton.pn_data_put_bool(@data, value)) + end + + # If the current node is a boolean, then it returns the value. Otherwise, + # it returns false. + def bool + Cproton.pn_data_get_bool(@data) + end + + # Puts an unsigned byte value. + # + # ==== Options + # + # * value - the unsigned byte value + def ubyte=(value) + check(Cproton.pn_data_put_ubyte(@data, value)) + end + + # If the current node is an unsigned byte, returns its value. Otherwise, + # it reutrns 0. + def ubyte + Cproton.pn_data_get_ubyte(@data) + end + + # Puts a byte value. + # + # ==== Options + # + # * value - the byte value + def byte=(value) + check(Cproton.pn_data_put_byte(@data, value)) + end + + # If the current node is an byte, returns its value. Otherwise, + # it returns 0. + def byte + Cproton.pn_data_get_byte(@data) + end + + # Puts an unsigned short value. + # + # ==== Options + # + # * value - the unsigned short value + def ushort=(value) + check(Cproton.pn_data_put_ushort(@data, value)) + end + + # If the current node is an unsigned short, returns its value. Otherwise, + # it returns 0. + def ushort + Cproton.pn_data_get_ushort(@data) + end + + # Puts a short value. + # + # ==== Options + # + # * value - the short value + def short=(value) + check(Cproton.pn_data_put_short(@data, value)) + end + + # If the current node is a short, returns its value. Otherwise, + # returns a 0. + def short + Cproton.pn_data_get_short(@data) + end + + # Puts an unsigned integer value. + # + # ==== Options + # + # * value - the unsigned integer value + def uint=(value) + raise TypeError if value.nil? + raise RangeError, "invalid uint: #{value}" if value < 0 + check(Cproton.pn_data_put_uint(@data, value)) + end + + # If the current node is an unsigned int, returns its value. Otherwise, + # returns 0. + def uint + Cproton.pn_data_get_uint(@data) + end + + # Puts an integer value. + # + # ==== Options + # + # * value - the integer value + def int=(value) + check(Cproton.pn_data_put_int(@data, value)) + end + + # If the current node is an integer, returns its value. Otherwise, + # returns 0. + def int + Cproton.pn_data_get_int(@data) + end + + # Puts a character value. + # + # ==== Options + # + # * value - the character value + def char=(value) + check(Cproton.pn_data_put_char(@data, value)) + end + + # If the current node is a character, returns its value. Otherwise, + # returns 0. + def char + Cproton.pn_data_get_char(@data) + end + + # Puts an unsigned long value. + # + # ==== Options + # + # * value - the unsigned long value + def ulong=(value) + raise TypeError if value.nil? + raise RangeError, "invalid ulong: #{value}" if value < 0 + check(Cproton.pn_data_put_ulong(@data, value)) + end + + # If the current node is an unsigned long, returns its value. Otherwise, + # returns 0. + def ulong + Cproton.pn_data_get_ulong(@data) + end + + # Puts a long value. + # + # ==== Options + # + # * value - the long value + def long=(value) + check(Cproton.pn_data_put_long(@data, value)) + end + + # If the current node is a long, returns its value. Otherwise, returns 0. + def long + Cproton.pn_data_get_long(@data) + end + + # Puts a timestamp value. + # + # ==== Options + # + # * value - the timestamp value + def timestamp=(value) + value = value.to_i if (!value.nil? && value.is_a?(Time)) + check(Cproton.pn_data_put_timestamp(@data, value)) + end + + # If the current node is a timestamp, returns its value. Otherwise, + # returns 0. + def timestamp + Cproton.pn_data_get_timestamp(@data) + end + + # Puts a float value. + # + # ==== Options + # + # * value - the float value + def float=(value) + check(Cproton.pn_data_put_float(@data, value)) + end + + # If the current node is a float, returns its value. Otherwise, + # returns 0. + def float + Cproton.pn_data_get_float(@data) + end + + # Puts a double value. + # + # ==== Options + # + # * value - the double value + def double=(value) + check(Cproton.pn_data_put_double(@data, value)) + end + + # If the current node is a double, returns its value. Otherwise, + # returns 0. + def double + Cproton.pn_data_get_double(@data) + end + + # Puts a decimal32 value. + # + # ==== Options + # + # * value - the decimal32 value + def decimal32=(value) + check(Cproton.pn_data_put_decimal32(@data, value)) + end + + # If the current node is a decimal32, returns its value. Otherwise, + # returns 0. + def decimal32 + Cproton.pn_data_get_decimal32(@data) + end + + # Puts a decimal64 value. + # + # ==== Options + # + # * value - the decimal64 value + def decimal64=(value) + check(Cproton.pn_data_put_decimal64(@data, value)) + end + + # If the current node is a decimal64, returns its value. Otherwise, + # it returns 0. + def decimal64 + Cproton.pn_data_get_decimal64(@data) + end + + # Puts a decimal128 value. + # + # ==== Options + # + # * value - the decimal128 value + def decimal128=(value) + raise TypeError, "invalid decimal128 value: #{value}" if value.nil? + value = value.to_s(16).rjust(32, "0") + bytes = [] + value.scan(/(..)/) {|v| bytes << v[0].to_i(16)} + check(Cproton.pn_data_put_decimal128(@data, bytes)) + end + + # If the current node is a decimal128, returns its value. Otherwise, + # returns 0. + def decimal128 + value = "" + Cproton.pn_data_get_decimal128(@data).each{|val| value += ("%02x" % val)} + value.to_i(16) + end + + # Puts a +UUID+ value. + # + # The UUID is expected to be in the format of a string or else a 128-bit + # integer value. + # + # ==== Options + # + # * value - the +UUID+ + # + # ==== Examples + # + # # set a uuid value from a string value + # require 'securerandom' + # @data.uuid = SecureRandom.uuid + # + # # or + # @data.uuid = "fd0289a5-8eec-4a08-9283-81d02c9d2fff" + # + # # set a uuid value from a 128-bit value + # @data.uuid = 0 # sets to 00000000-0000-0000-0000-000000000000 + # + def uuid=(value) + raise ArgumentError, "invalid uuid: #{value}" if value.nil? + + # if the uuid that was submitted was numeric value, then translated + # it into a hex string, otherwise assume it was a string represtation + # and attempt to decode it + if value.is_a? Numeric + value = "%032x" % value + else + raise ArgumentError, "invalid uuid: #{value}" if !valid_uuid?(value) + + value = (value[0, 8] + + value[9, 4] + + value[14, 4] + + value[19, 4] + + value[24, 12]) + end + bytes = [] + value.scan(/(..)/) {|v| bytes << v[0].to_i(16)} + check(Cproton.pn_data_put_uuid(@data, bytes)) + end + + # If the current value is a +UUID+, returns its value. Otherwise, + # it returns nil. + def uuid + value = "" + Cproton.pn_data_get_uuid(@data).each{|val| value += ("%02x" % val)} + value.insert(8, "-").insert(13, "-").insert(18, "-").insert(23, "-") + end + + # Puts a binary value. + # + # ==== Options + # + # * value - the binary value + def binary=(value) + check(Cproton.pn_data_put_binary(@data, value)) + end + + # If the current node is binary, returns its value. Otherwise, it returns + # an empty string (""). + def binary + Qpid::Proton::BinaryString.new(Cproton.pn_data_get_binary(@data)) + end + + # Puts a unicode string value. + # + # *NOTE:* A nil value is stored as an empty string rather than as a nil. + # + # ==== Options + # + # * value - the unicode string value + def string=(value) + check(Cproton.pn_data_put_string(@data, value)) + end + + # If the current node is a string, returns its value. Otherwise, it + # returns an empty string (""). + def string + Qpid::Proton::UTFString.new(Cproton.pn_data_get_string(@data)) + end + + # Puts a symbolic value. + # + # ==== Options + # + # * value - the symbol name + def symbol=(value) + check(Cproton.pn_data_put_symbol(@data, value)) + end + + # If the current node is a symbol, returns its value. Otherwise, it + # returns an empty string (""). + def symbol + Cproton.pn_data_get_symbol(@data) + end + + # Get the current value as a single object. + def get + type.get(self); + end + + # Put value as an object of type type_ + def put(value, type_); + type_.put(self, value); + end + + private + + def valid_uuid?(value) + # ensure that the UUID is in the right format + # xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + value =~ /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ + end + + def check(err) # :nodoc: + if err < 0 + raise DataError, "[#{err}]: #{Cproton.pn_data_error(@data)}" + else + return err + end + end + end + end +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/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 new file mode 100644 index 0000000..9189cbc --- /dev/null +++ b/proton-c/bindings/ruby/lib/codec/mapping.rb @@ -0,0 +1,170 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + # Maps between Proton types and their Ruby native language counterparts. + # + class Mapping + + attr_reader :code + attr_reader :put_method + attr_reader :get_method + + # Creates a new mapping. + # + # ==== Arguments + # + # * code - the AMQP code for this type + # * name - the AMQP name for this type + # * klasses - the Ruby classes for this type + # * getter - overrides the get method for the type + def initialize(code, name, klasses = nil, getter = nil) + + @debug = (name == "bool") + + @code = code + @name = name + + @@by_preferred ||= {} + @@by_code ||= {} + @@by_code["#{code}"] = self + @@by_name ||= {} + @@by_name[name] = self + @@by_class ||= {} + + unless klasses.nil? + klasses.each do |klass| + raise "entry exists for #{klass}" if @@by_class.keys.include? klass + @@by_class[klass] = self unless klass.nil? + end + end + + @put_method = (name + "=").intern + + if getter.nil? + @get_method = name.intern + else + @get_method = getter.intern + end + end + + def to_s; @name; end + + def put(data, value) + data.__send__(@put_method, value) + end + + def get(data) + data.__send__(@get_method) + end + + def self.for_class(klass) # :nodoc: + @@by_class[klass] + end + + def self.for_code(code) + @@by_code["#{code}"] + end + + end + + NULL = Mapping.new(Cproton::PN_NULL, "null", [NilClass], "nil?") + BOOL = Mapping.new(Cproton::PN_BOOL, "bool", [TrueClass, FalseClass], "bool") + UBYTE = Mapping.new(Cproton::PN_UBYTE, "ubyte") + BYTE = Mapping.new(Cproton::PN_BYTE, "byte") + USHORT = Mapping.new(Cproton::PN_USHORT, "ushort") + SHORT = Mapping.new(Cproton::PN_SHORT, "short") + UINT = Mapping.new(Cproton::PN_UINT, "uint") + INT = Mapping.new(Cproton::PN_INT, "int") + CHAR = Mapping.new(Cproton::PN_CHAR, "char") + ULONG = Mapping.new(Cproton::PN_ULONG, "ulong") + LONG = Mapping.new(Cproton::PN_LONG, "long", [Fixnum, Bignum]) + TIMESTAMP = Mapping.new(Cproton::PN_TIMESTAMP, "timestamp", [Date, Time]) + FLOAT = Mapping.new(Cproton::PN_FLOAT, "float") + DOUBLE = Mapping.new(Cproton::PN_DOUBLE, "double", [Float]) + DECIMAL32 = Mapping.new(Cproton::PN_DECIMAL32, "decimal32") + DECIMAL64 = Mapping.new(Cproton::PN_DECIMAL64, "decimal64") + 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, + UTFString, + BinaryString]) + + class << STRING # :nodoc: + def put(data, value) + # if we have a symbol then convert it to a string + value = value.to_s if value.is_a?(Symbol) + + isutf = false + + if value.is_a?(Qpid::Proton::UTFString) + isutf = true + else + # For Ruby 1.8 we will just treat all strings as binary. + # For Ruby 1.9+ we can check the encoding first to see what it is + if RUBY_VERSION >= "1.9" + # If the string is ASCII-8BIT then treat is as binary. Otherwise, + # try to convert it to UTF-8 and, if successful, send as that. + if value.encoding != Encoding::ASCII_8BIT && + value.encode(Encoding::UTF_8).valid_encoding? + isutf = true + end + end + end + + 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::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") + + class << MAP # :nodoc: + def put(data, map, options = {}) + data.put_map + data.enter + map.each_pair do |key, value| + if options[:keys] == :SYMBOL + SYMBOL.put(data, key) + else + Mapping.for_class(key.class).put(data, key) + end + + if value.nil? + data.null + else + Mapping.for_class(value.class).put(data, value) + end + end + data.exit + end + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/proton-c/bindings/ruby/lib/core/exceptions.rb ---------------------------------------------------------------------- diff --git a/proton-c/bindings/ruby/lib/core/exceptions.rb b/proton-c/bindings/ruby/lib/core/exceptions.rb new file mode 100644 index 0000000..189f574 --- /dev/null +++ b/proton-c/bindings/ruby/lib/core/exceptions.rb @@ -0,0 +1,85 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + module Error + + NONE = 0 + EOS = Cproton::PN_EOS + ERROR = Cproton::PN_ERR + OVERFLOW = Cproton::PN_OVERFLOW + UNDERFLOW = Cproton::PN_UNDERFLOW + STATE = Cproton::PN_STATE_ERR + ARGUMENT = Cproton::PN_ARG_ERR + TIMEOUT = Cproton::PN_TIMEOUT + INTERRUPTED = Cproton::PN_INTR + INPROGRESS = Cproton::PN_INPROGRESS + + end + + # Represents a generic error at the messaging level. + # + class ProtonError < RuntimeError + end + + # Represents an end-of-stream error while messaging. + # + class EOSError < ProtonError + end + + # Represents a data overflow exception while messaging. + # + class OverflowError < ProtonError + end + + # Represents a data underflow exception while messaging. + # + class UnderflowError < ProtonError + end + + # Represents an invalid, missing or illegal argument while messaging. + # + class ArgumentError < ProtonError + end + + # Represents that the client has got into an unexpected state during + # messaging. + # + class StateError < ProtonError + end + + # Represents a timeout during messaging. + # + class TimeoutError < ProtonError + end + + # Represents an interrupting during a blocking I/O operation. + # + class InterruptedError < ProtonError + end + + class InProgressError < ProtonError + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/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 new file mode 100644 index 0000000..144990b --- /dev/null +++ b/proton-c/bindings/ruby/lib/core/message.rb @@ -0,0 +1,621 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + # A Message represents an addressable quantity of data. + # + # ==== Message Body + # + # The message body can be set using the #body= method. The message will + # then attempt to determine how exactly to encode the content. + # + # ==== Examples + # + # To create a message for sending: + # + # # send a simple text message + # msg = Qpid::Proton::Message.new + # msg.body = "STATE: update" + # + # # send a binary chunk of data + # data = File.binread("/home/qpid/binfile.tar.gz") + # msg = Qpid::Proton::Message.new + # msg.body = Qpid::Proton::BinaryString.new(data) + # + class Message + + # Decodes a message from supplied AMQP data and returns the number + # of bytes consumed. + # + # ==== Options + # + # * encoded - the encoded data + # + def decode(encoded) + check(Cproton.pn_message_decode(@impl, encoded, encoded.length)) + + post_decode + end + + def post_decode # :nodoc: + # decode elements from the message + @properties = {} + props = Qpid::Proton::Data.new(Cproton::pn_message_properties(@impl)) + if props.next + @properties = props.type.get(props) + end + @instructions = nil + insts = Qpid::Proton::Data.new(Cproton::pn_message_instructions(@impl)) + if insts.next + @instructions = insts.type.get(insts) + end + @annotations = nil + annts = Qpid::Proton::Data.new(Cproton::pn_message_annotations(@impl)) + if annts.next + @annotations = annts.type.get(annts) + end + @body = nil + body = Qpid::Proton::Data.new(Cproton::pn_message_body(@impl)) + if body.next + @body = body.type.get(body) + end + end + + # Encodes the message. + def encode + pre_encode + size = 16 + loop do + error, data = Cproton::pn_message_encode(@impl, size) + if error == Qpid::Proton::Error::OVERFLOW + size *= 2 + else + check(error) + return data + end + end + end + + def pre_encode # :nodoc: + # encode elements from the message + props = Qpid::Proton::Data.new(Cproton::pn_message_properties(@impl)) + props.clear + Qpid::Proton::Mapping.for_class(@properties.class).put(props, @properties) unless @properties.empty? + insts = Qpid::Proton::Data.new(Cproton::pn_message_instructions(@impl)) + insts.clear + if !@instructions.nil? + mapping = Qpid::Proton::Mapping.for_class(@instructions.class) + mapping.put(insts, @instructions) + end + annts = Qpid::Proton::Data.new(Cproton::pn_message_annotations(@impl)) + annts.clear + if !@annotations.nil? + mapping = Qpid::Proton::Mapping.for_class(@annotations.class) + mapping.put(annts, @annotations, :keys => :SYMBOL) + end + body = Qpid::Proton::Data.new(Cproton::pn_message_body(@impl)) + body.clear + if !@body.nil? + mapping = Qpid::Proton::Mapping.for_class(@body.class) + mapping.put(body, @body) + end + end + + # Creates a new +Message+ instance. + def initialize + @impl = Cproton.pn_message + ObjectSpace.define_finalizer(self, self.class.finalize!(@impl)) + @properties = {} + @instructions = {} + @annotations = {} + @body = nil + end + + def to_s + tmp = Cproton.pn_string("") + Cproton.pn_inspect(@impl, tmp) + result = Cproton.pn_string_get(tmp) + Cproton.pn_free(tmp) + return result + end + + # Invoked by garbage collection to clean up resources used + # by the underlying message implementation. + def self.finalize!(impl) # :nodoc: + proc { + Cproton.pn_message_free(impl) + } + end + + # Returns the underlying message implementation. + def impl # :nodoc: + @impl + end + + # Clears the state of the +Message+. This allows a single instance of + # +Message+ to be reused. + # + def clear + Cproton.pn_message_clear(@impl) + @properties.clear unless @properties.nil? + @instructions.clear unless @instructions.nil? + @annotations.clear unless @annotations.nil? + @body = nil + end + + # Returns the most recent error number. + # + def errno + Cproton.pn_message_errno(@impl) + end + + # Returns the most recent error message. + # + def error + Cproton.pn_error_text(Cproton.pn_message_error(@impl)) + end + + # Returns whether there is currently an error reported. + # + def error? + !Cproton.pn_message_errno(@impl).zero? + end + + # Sets the durable flag. + # + # See ::durable for more details on message durability. + # + # ==== Options + # + # * state - the durable state + # + def durable=(state) + raise TypeError.new("state cannot be nil") if state.nil? + Cproton.pn_message_set_durable(@impl, state) + end + + # Returns the durable property. + # + # The durable property indicates that the emessage should be held durably + # by any intermediaries taking responsibility for the message. + # + # ==== Examples + # + # msg = Qpid::Proton::Message.new + # msg.durable = true + # + def durable + Cproton.pn_message_is_durable(@impl) + end + + # Sets the priority. + # + # +NOTE:+ Priority values are limited to the range [0,255]. + # + # ==== Options + # + # * priority - the priority value + # + def priority=(priority) + raise TypeError.new("invalid priority: #{priority}") if priority.nil? || !([Float, Fixnum].include?(priority.class)) + raise RangeError.new("priority out of range: #{priority}") if ((priority > 255) || (priority < 0)) + Cproton.pn_message_set_priority(@impl, priority.floor) + end + + # Returns the priority. + # + def priority + Cproton.pn_message_get_priority(@impl) + end + + # Sets the time-to-live for the message. + # + # ==== Options + # + # * time - the time in milliseconds + # + def ttl=(time) + raise TypeError.new("invalid ttl: #{time}") if time.nil? || !([Float, Fixnum].include?(time.class)) + raise RangeError.new("time out of range: #{time}") if ((time < 0)) + Cproton.pn_message_set_ttl(@impl, time.floor) + end + + # Returns the time-to-live, in milliseconds. + # + def ttl + Cproton.pn_message_get_ttl(@impl) + end + + # Sets whether this is the first time the message was acquired. + # + # See ::first_acquirer? for more details. + # + # ==== Options + # + # * state - true if claiming the message + # + def first_acquirer=(state) + raise TypeError.new("invalid state: #{state}") if state.nil? || !([TrueClass, FalseClass].include?(state.class)) + Cproton.pn_message_set_first_acquirer(@impl, state) + end + + # Sets the delivery count for the message. + # + # See ::delivery_count for more details. + # + # ==== Options + # + # * count - the delivery count + # + def delivery_count=(count) + raise ArgumentError.new("invalid count: #{count}") if count.nil? || !([Float, Fixnum].include?(count.class)) + raise RangeError.new("count out of range: #{count}") if count < 0 + + Cproton.pn_message_set_delivery_count(@impl, count.floor) + end + + # Returns the delivery count for the message. + # + # This is the number of delivery attempts for the given message. + # + def delivery_count + Cproton.pn_message_get_delivery_count(@impl) + end + + # Returns whether this is the first acquirer. + # + # + def first_acquirer? + Cproton.pn_message_is_first_acquirer(@impl) + end + + # Sets the message id. + # + # ==== Options + # + # * id = the id + # + def id=(id) + Cproton.pn_message_set_id(@impl, id) + end + + # Returns the message id. + # + def id + Cproton.pn_message_get_id(@impl) + end + + # Sets the user id. + # + # ==== Options + # + # * id - the user id + # + def user_id=(id) + Cproton.pn_message_set_user_id(@impl, id) + end + + # Returns the user id. + # + def user_id + Cproton.pn_message_get_user_id(@impl) + end + + # Sets the destination address. + # + # ==== Options + # + # * address - the address + # + def address=(address) + Cproton.pn_message_set_address(@impl, address) + end + + # Returns the destination address. + # + def address + Cproton.pn_message_get_address(@impl) + end + + # Sets the subject. + # + # ==== Options + # + # * subject - the subject + # + def subject=(subject) + Cproton.pn_message_set_subject(@impl, subject) + end + + # Returns the subject + # + def subject + Cproton.pn_message_get_subject(@impl) + end + + # Sets the reply-to address. + # + # ==== Options + # + # * address - the reply-to address + # + def reply_to=(address) + Cproton.pn_message_set_reply_to(@impl, address) + end + + # Returns the reply-to address + # + def reply_to + Cproton.pn_message_get_reply_to(@impl) + end + + # Sets the correlation id. + # + # ==== Options + # + # * id - the correlation id + # + def correlation_id=(id) + Cproton.pn_message_set_correlation_id(@impl, id) + end + + # Returns the correlation id. + # + def correlation_id + Cproton.pn_message_get_correlation_id(@impl) + end + + # Sets the message format. + # + # See MessageFormat for more details on formats. + # + # *Warning:* This method has been deprecated. + # + # ==== Options + # + # * format - the format + # + def format=(format) + raise TypeError.new("invalid message format: #{format}") if (format.nil? || !format.kind_of?(Qpid::Proton::MessageFormat)) + Cproton.pn_message_set_format(@impl, format.value) + end + + # Returns the message format + # + # *Warning:* This method has been deprecated. + # + # ==== Note + # + # This method is now deprecated. + # + def format + Qpid::Proton::MessageFormat.by_value(Cproton.pn_message_get_format(@impl)) + end + + # Sets the content type. + # + # ==== Options + # + # * content_type - the content type + # + def content_type=(content_type) + Cproton.pn_message_set_content_type(@impl, content_type) + end + + # Returns the content type + # + def content_type + Cproton.pn_message_get_content_type(@impl) + end + + # Sets the content encoding type. + # + # ==== Options + # + # * encoding - the content encoding + # + def content_encoding=(encoding) + Cproton.pn_message_set_content_encoding(@impl, encoding) + end + + # Returns the content encoding type. + # + def content_encoding + Cproton.pn_message_get_content_encoding(@impl) + end + + # Sets the expiration time. + # + # ==== Options + # + # * time - the expiry time + # + def expires=(time) + raise TypeError.new("invalid expiry time: #{time}") if time.nil? + raise ArgumentError.new("expiry time cannot be negative: #{time}") if time < 0 + Cproton.pn_message_set_expiry_time(@impl, time) + end + + # Returns the expiration time. + # + def expires + Cproton.pn_message_get_expiry_time(@impl) + end + + # Sets the creation time. + # + # ==== Options + # + # * time - the creation time + # + def creation_time=(time) + raise TypeError.new("invalid time: #{time}") if time.nil? + raise ArgumentError.new("time cannot be negative") if time < 0 + Cproton.pn_message_set_creation_time(@impl, time) + end + + # Returns the creation time. + # + def creation_time + Cproton.pn_message_get_creation_time(@impl) + end + + # Sets the group id. + # + # ==== Options + # + # * id - the group id + # + def group_id=(id) + Cproton.pn_message_set_group_id(@impl, id) + end + + # Returns the group id. + # + def group_id + Cproton.pn_message_get_group_id(@impl) + end + + # Sets the group sequence number. + # + # ==== Options + # + # * seq - the sequence number + # + def group_sequence=(seq) + raise TypeError.new("invalid seq: #{seq}") if seq.nil? + Cproton.pn_message_set_group_sequence(@impl, seq) + end + + # Returns the group sequence number. + # + def group_sequence + Cproton.pn_message_get_group_sequence(@impl) + end + + # Sets the reply-to group id. + # + # ==== Options + # + # * id - the id + # + def reply_to_group_id=(id) + Cproton.pn_message_set_reply_to_group_id(@impl, id) + end + + # Returns the reply-to group id. + # + def reply_to_group_id + Cproton.pn_message_get_reply_to_group_id(@impl) + end + + # Returns the list of property names for associated with this message. + # + # ==== Examples + # + # msg.properties.each do |name| + # end + # + def properties + @properties + end + + # Replaces the entire set of properties with the specified hash. + # + def properties=(properties) + @properties = properties + end + + # Assigns the value given to the named property. + # + # ==== Arguments + # + # * name - the property name + # * value - the property value + # + def []=(name, value) + @properties[name] = value + end + + # Retrieves the value for the specified property name. If not found, then + # it returns nil. + # + def [](name) + @properties[name] + end + + # Deletes the named property. + # + def delete_property(name) + @properties.delete(name) + end + + # Returns the instructions for this message. + # + def instructions + @instructions + end + + # Assigns instructions to this message. + # + def instructions=(instr) + @instructions = instr + end + + # Returns the annotations for this message. + # + def annotations + @annotations + end + + # Assigns annotations to this message. + # + def annotations=(annotations) + @annotations = annotations + end + + # Returns the body property of the message. + # + def body + @body + end + + # Assigns a new value to the body of the message. + # + def body=(body) + @body = body + end + + private + + def check(err) # :nodoc: + if err < 0 + raise DataError, "[#{err}]: #{Cproton.pn_message_error(@impl)}" + else + return err + end + end + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/proton-c/bindings/ruby/lib/messenger/filters.rb ---------------------------------------------------------------------- diff --git a/proton-c/bindings/ruby/lib/messenger/filters.rb b/proton-c/bindings/ruby/lib/messenger/filters.rb new file mode 100644 index 0000000..370d017 --- /dev/null +++ b/proton-c/bindings/ruby/lib/messenger/filters.rb @@ -0,0 +1,67 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + module Filters + + def self.included(base) + base.class_eval do + extend ClassMethods + end + end + + module ClassMethods + + def method_added(method_name) + @@hooked_methods ||= [] + return if @@hooked_methods.include?(method_name) + @@hooked_methods << method_name + hooks = @@before_hooks[method_name] + return if hooks.nil? + orig_method = instance_method(method_name) + define_method(method_name) do |*args, &block| + hooks = @@before_hooks[method_name] + hooks.each do |hook| + method(hook).call + end + + orig_method.bind(self).call(*args, &block) + end + end + + def call_before(before_method, *methods) + @@before_hooks ||= {} + methods.each do |method| + hooks = @@before_hooks[method] || [] + raise "Repeat filter: #{before_method}" if hooks.include? before_method + hooks << before_method + @@before_hooks[method] = hooks + end + end + + end + + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/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 new file mode 100644 index 0000000..5a16c50 --- /dev/null +++ b/proton-c/bindings/ruby/lib/messenger/messenger.rb @@ -0,0 +1,702 @@ +# +# 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 # :nodoc: + + module Proton # :nodoc: + + # The +Messenger+ class defines a high level interface for + # sending and receiving Messages. Every Messenger contains + # a single logical queue of incoming messages and a single + # logical queue of outgoing messages. These messages in these + # queues may be destined for, or originate from, a variety of + # addresses. + # + # The messenger interface is single-threaded. All methods + # except one ( #interrupt ) are intended to be used from within + # the messenger thread. + # + # === Sending & Receiving Messages + # + # The Messenger class works in conjuction with the Message class. The + # Message class is a mutable holder of message content. + # + # The put method copies its Message to the outgoing queue, and may + # send queued messages if it can do so without blocking. The send + # method blocks until it has sent the requested number of messages, + # or until a timeout interrupts the attempt. + # + # Similarly, the recv method receives messages into the incoming + # queue, and may block as it attempts to receive the requested number + # of messages, or until timeout is reached. It may receive fewer + # than the requested number. The get method pops the + # eldest Message off the incoming queue and copies it into the Message + # object that you supply. It will not block. + # + # The blocking attribute allows you to turn off blocking behavior entirely, + # in which case send and recv will do whatever they can without + # blocking, and then return. You can then look at the number + # of incoming and outgoing messages to see how much outstanding work + # still remains. + # + class Messenger + + include Qpid::Proton::ExceptionHandling + + can_raise_exception [:send, :receive, :password=, :start, :stop, + :perform_put, :perform_get, :interrupt, + :route, :rewrite, :accept, :reject, + :incoming_window=, :outgoing_window=] + + # Creates a new +Messenger+. + # + # The +name+ parameter is optional. If one is not provided then + # a unique name is generated. + # + # ==== Options + # + # * name - the name (def. nil) + # + def initialize(name = nil) + @impl = Cproton.pn_messenger(name) + @selectables = {} + ObjectSpace.define_finalizer(self, self.class.finalize!(@impl)) + end + + def self.finalize!(impl) # :nodoc: + proc { + Cproton.pn_messenger_free(impl) + } + end + + # Returns the name. + # + def name + Cproton.pn_messenger_name(@impl) + end + + # This property contains the password for the Messenger.private_key + # file, or +nil+ if the file is not encrypted. + # + # ==== Arguments + # + # * password - the password + # + def password=(password) + Cproton.pn_messenger_set_password(@impl, password) + end + + # Returns the password property for the Messenger.private_key file. + # + def password + Cproton.pn_messenger_get_password(@impl) + end + + # Sets the timeout period, in milliseconds. + # + # A negative timeout period implies an infinite timeout. + # + # ==== Options + # + # * timeout - the timeout period + # + def timeout=(timeout) + raise TypeError.new("invalid timeout: #{timeout}") if timeout.nil? + Cproton.pn_messenger_set_timeout(@impl, timeout) + end + + # Returns the timeout period + # + def timeout + Cproton.pn_messenger_get_timeout(@impl) + end + + # Returns true if blocking mode is enabled. + # + # Enable or disable blocking behavior during message sending + # and receiving. This affects every blocking call, with the + # exception of work(). Currently, the affected calls are + # send, recv, and stop. + def blocking? + Cproton.pn_messenger_is_blocking(@impl) + end + + # Sets the blocking mode. + def blocking=(blocking) + Cproton.pn_messenger_set_blocking(@impl, blocking) + end + + # Returns true if passive mode is enabled. + # + def passive? + Cproton.pn_messenger_is_passive(@impl) + end + + # Turns passive mode on or off. + # + # When set to passive mode, Messenger will not attempt to perform I/O + # operations internally. In this mode it is necesssary to use the + # Selectable type to drive any I/O needed to perform requestioned + # actions. + # + # In this mode Messenger will never block. + # + def passive=(mode) + Cproton.pn_messenger_set_passive(@impl, mode) + end + + def deadline + tstamp = Cproton.pn_messenger_deadline(@impl) + return tstamp / 1000.0 unless tstamp.nil? + end + + # Reports whether an error occurred. + # + def error? + !Cproton.pn_messenger_errno(@impl).zero? + end + + # Returns the most recent error number. + # + def errno + Cproton.pn_messenger_errno(@impl) + end + + # Returns the most recent error message. + # + def error + Cproton.pn_error_text(Cproton.pn_messenger_error(@impl)) + end + + # Clears the current error state. + # + def clear_error + error = Cproton.pn_messenger_error(@impl) + unless error.nil? + Cproton.pn_error_clear(error) + end + end + + # Currently a no-op placeholder. + # For future compatibility, do not send or recv messages + # before starting the +Messenger+. + # + def start + Cproton.pn_messenger_start(@impl) + end + + # Stops the +Messenger+, preventing it from sending or receiving + # any more messages. + # + def stop + Cproton.pn_messenger_stop(@impl) + end + + # Returns true if a Messenger is in the stopped state. + # This function does not block. + # + def stopped? + Cproton.pn_messenger_stopped(@impl) + end + + # Subscribes the Messenger to messages originating from the + # specified source. The source is an address as specified in the + # Messenger introduction with the following addition. If the + # domain portion of the address begins with the '~' character, the + # Messenger will interpret the domain as host/port, bind to it, + # and listen for incoming messages. For example "~0.0.0.0", + # "amqp://~0.0.0.0" will all bind to any local interface and + # listen for incoming messages. An address of "amqps://~0.0.0.0" + # will only permit incoming SSL connections. + # + # ==== Options + # + # * address - the source address to be subscribe + # * timeout - an optional time-to-live value, in seconds, for the + # subscription + # + def subscribe(address, timeout=0) + raise TypeError.new("invalid address: #{address}") if address.nil? + subscription = Cproton.pn_messenger_subscribe_ttl(@impl, address, timeout) + raise Qpid::Proton::ProtonError.new("Subscribe failed") if subscription.nil? + Qpid::Proton::Subscription.new(subscription) + end + + # Path to a certificate file for the +Messenger+. + # + # This certificate is used when the +Messenger+ accepts or establishes + # SSL/TLS connections. This property must be specified for the + # Messenger to accept incoming SSL/TLS connections and to establish + # client authenticated outgoing SSL/TLS connection. Non client authenticated + # outgoing SSL/TLS connections do not require this property. + # + # ==== Options + # + # * certificate - the certificate + # + def certificate=(certificate) + Cproton.pn_messenger_set_certificate(@impl, certificate) + end + + # Returns the path to a certificate file. + # + def certificate + Cproton.pn_messenger_get_certificate(@impl) + end + + # Path to a private key file for the +Messenger+. + # + # The property must be specified for the +Messenger+ to accept incoming + # SSL/TLS connections and to establish client authenticated outgoing + # SSL/TLS connections. Non client authenticated SSL/TLS connections + # do not require this property. + # + # ==== Options + # + # * key - the key file + # + def private_key=(key) + Cproton.pn_messenger_set_private_key(@impl, key) + end + + # Returns the path to a private key file. + # + def private_key + Cproton.pn_messenger_get_private_key(@impl) + end + + # A path to a database of trusted certificates for use in verifying the + # peer on an SSL/TLS connection. If this property is +nil+, then the + # peer will not be verified. + # + # ==== Options + # + # * certificates - the certificates path + # + def trusted_certificates=(certificates) + Cproton.pn_messenger_set_trusted_certificates(@impl,certificates) + end + + # The path to the databse of trusted certificates. + # + def trusted_certificates + Cproton.pn_messenger_get_trusted_certificates(@impl) + end + + # Places the content contained in the message onto the outgoing + # queue of the Messenger. + # + # This method will never block, however it will send any unblocked + # Messages in the outgoing queue immediately and leave any blocked + # Messages remaining in the outgoing queue. + # The send call may then be used to block until the outgoing queue + # is empty. The outgoing attribute may be used to check the depth + # of the outgoing queue. + # + # ==== Options + # + # * message - the message + # + def put(message) + raise TypeError.new("invalid message: #{message}") if message.nil? + raise ArgumentError.new("invalid message type: #{message.class}") unless message.kind_of?(Message) + # encode the message first + message.pre_encode + perform_put(message) + return outgoing_tracker + end + + private + + def perform_put(message) # :nodoc: + Cproton.pn_messenger_put(@impl, message.impl) + end + + public + + + # This call will block until the indicated number of messages + # have been sent, or until the operation times out. + # If n is -1 this call will block until all outgoing messages + # have been sent. If n is 0 then this call will send whatever + # it can without blocking. + # + def send(n = -1) + Cproton.pn_messenger_send(@impl, n) + end + + # Moves the message from the head of the incoming message queue into + # the supplied message object. Any content in the supplied message + # will be overwritten. + # A tracker for the incoming Message is returned. The tracker can + # later be used to communicate your acceptance or rejection of the + # Message. + # + # If no message is provided in the argument, then one is created. In + # either case, the one returned will be the fetched message. + # + # ==== Options + # + # * msg - the (optional) +Message+ instance to be used + # + def get(msg = nil) + msg_impl = nil + if msg.nil? then + msg_impl = nil + else + msg_impl = msg.impl + end + perform_get(msg_impl) + msg.post_decode unless msg.nil? + return incoming_tracker + end + + private + + def perform_get(msg) # :nodoc: + Cproton.pn_messenger_get(@impl, msg) + end + + public + + # Receives up to limit messages into the incoming queue. If no value + # for limit is supplied, this call will receive as many messages as it + # can buffer internally. If the Messenger is in blocking mode, this + # call will block until at least one Message is available in the + # incoming queue. + # + # Options ==== + # + # * limit - the maximum number of messages to receive + # + def receive(limit = -1) + Cproton.pn_messenger_recv(@impl, limit) + end + + # Returns true if the messenger is currently receiving data. + def receiving? + Cproton.pn_messenger_receiving(@impl) + end + + # Attempts interrupting of the messenger thread. + # + # The Messenger interface is single-threaded, and this is the only + # function intended to be called from outside of is thread. + # + # Call this from a non-Messenger thread to interrupt it while it + # is blocking. This will cause a ::InterruptError to be raised. + # + # If there is no currently blocking call, then the next blocking + # call will be affected, even if it is within the same thread that + # originated the interrupt. + # + def interrupt + Cproton.pn_messenger_interrupt(@impl) + end + + # Sends or receives any outstanding messages queued for a Messenger. + # + # This will block for the indicated timeout. This method may also do I/O + # other than sending and receiving messages. For example, closing + # connections after stop() has been called. + # + def work(timeout=-1) + err = Cproton.pn_messenger_work(@impl, timeout) + if (err == Cproton::PN_TIMEOUT) then + return false + else + check_for_error(err) + return true + end + end + + # Returns the number messages in the outgoing queue that have not been + # transmitted. + # + def outgoing + Cproton.pn_messenger_outgoing(@impl) + end + + # Returns the number of messages in the incoming queue that have not + # been retrieved. + # + def incoming + Cproton.pn_messenger_incoming(@impl) + end + + # Adds a routing rule to the Messenger's internal routing table. + # + # The route procedure may be used to influence how a Messenger will + # internally treat a given address or class of addresses. Every call + # to the route procedure will result in Messenger appending a routing + # rule to its internal routing table. + # + # Whenever a Message is presented to a Messenger for delivery, it + # will match the address of this message against the set of routing + # rules in order. The first rule to match will be triggered, and + # instead of routing based on the address presented in the message, + # the Messenger will route based on the address supplied in the rule. + # + # The pattern matching syntax supports two types of matches, a '%' + # will match any character except a '/', and a '*' will match any + # character including a '/'. + # + # A routing address is specified as a normal AMQP address, however it + # may additionally use substitution variables from the pattern match + # that triggered the rule. + # + # ==== Arguments + # + # * pattern - the address pattern + # * address - the target address + # + # ==== Examples + # + # # route messages sent to foo to the destionaty amqp://foo.com + # messenger.route("foo", "amqp://foo.com") + # + # # any message to foobar will be routed to amqp://foo.com/bar + # messenger.route("foobar", "amqp://foo.com/bar") + # + # # any message to bar/<path> will be routed to the same path within + # # the amqp://bar.com domain + # messenger.route("bar/*", "amqp://bar.com/$1") + # + # # route all Message objects over TLS + # messenger.route("amqp:*", "amqps:$1") + # + # # supply credentials for foo + # messenger.route("amqp://foo.com/*", "amqp://user:passw...@foo.com/$1") + # + # # supply credentials for all domains + # messenger.route("amqp://*", "amqp://user:password@$1") + # + # # route all addresses through a single proxy while preserving the + # # original destination + # messenger.route("amqp://%$/*", "amqp://user:password@proxy/$1/$2") + # + # # route any address through a single broker + # messenger.route("*", "amqp://user:password@broker/$1") + # + def route(pattern, address) + Cproton.pn_messenger_route(@impl, pattern, address) + end + + # Similar to #route, except that the destination of + # the Message is determined before the message address is rewritten. + # + # The outgoing address is only rewritten after routing has been + # finalized. If a message has an outgoing address of + # "amqp://0.0.0.0:5678", and a rewriting rule that changes its + # outgoing address to "foo", it will still arrive at the peer that + # is listening on "amqp://0.0.0.0:5678", but when it arrives there, + # the receiver will see its outgoing address as "foo". + # + # The default rewrite rule removes username and password from addresses + # before they are transmitted. + # + # ==== Arguments + # + # * pattern - the outgoing address + # * address - the target address + # + def rewrite(pattern, address) + Cproton.pn_messenger_rewrite(@impl, pattern, address) + end + + def selectable + impl = Cproton.pn_messenger_selectable(@impl) + + # if we don't have any selectables, then return + return nil if impl.nil? + + fd = Cproton.pn_selectable_fd(impl) + + selectable = @selectables[fd] + if selectable.nil? + selectable = Selectable.new(self, impl) + @selectables[fd] = selectable + end + return selectable + end + + # Returns a +Tracker+ for the message most recently sent via the put + # method. + # + def outgoing_tracker + impl = Cproton.pn_messenger_outgoing_tracker(@impl) + return nil if impl == -1 + Qpid::Proton::Tracker.new(impl) + end + + # Returns a +Tracker+ for the most recently received message. + # + def incoming_tracker + impl = Cproton.pn_messenger_incoming_tracker(@impl) + return nil if impl == -1 + Qpid::Proton::Tracker.new(impl) + end + + # Signal the sender that you have acted on the Message + # pointed to by the tracker. If no tracker is supplied, + # then all messages that have been returned by the get + # method are accepted, except those that have already been + # auto-settled by passing beyond your incoming window size. + # + # ==== Options + # + # * tracker - the tracker + # + def accept(tracker = nil) + raise TypeError.new("invalid tracker: #{tracker}") unless tracker.nil? or valid_tracker?(tracker) + if tracker.nil? then + tracker = self.incoming_tracker + flag = Cproton::PN_CUMULATIVE + else + flag = 0 + end + Cproton.pn_messenger_accept(@impl, tracker.impl, flag) + end + + # Rejects the incoming message identified by the tracker. + # If no tracker is supplied, all messages that have been returned + # by the get method are rejected, except those that have already + # been auto-settled by passing beyond your outgoing window size. + # + # ==== Options + # + # * tracker - the tracker + # + def reject(tracker) + raise TypeError.new("invalid tracker: #{tracker}") unless tracker.nil? or valid_tracker?(tracker) + if tracker.nil? then + tracker = self.incoming_tracker + flag = Cproton::PN_CUMULATIVE + else + flag = 0 + end + Cproton.pn_messenger_reject(@impl, tracker.impl, flag) + end + + # Gets the last known remote state of the delivery associated with + # the given tracker, as long as the Message is still within your + # outgoing window. (Also works on incoming messages that are still + # within your incoming queue. See TrackerStatus for details on the + # values returned. + # + # ==== Options + # + # * tracker - the tracker + # + def status(tracker) + raise TypeError.new("invalid tracker: #{tracker}") unless valid_tracker?(tracker) + Qpid::Proton::TrackerStatus.by_value(Cproton.pn_messenger_status(@impl, tracker.impl)) + end + + # Frees a Messenger from tracking the status associated + # with a given tracker. If you don't supply a tracker, all + # outgoing messages up to the most recent will be settled. + # + # ==== Options + # + # * tracker - the tracker + # + # ==== Examples + # + def settle(tracker) + raise TypeError.new("invalid tracker: #{tracker}") unless valid_tracker?(tracker) + if tracker.nil? then + tracker = self.incoming_tracker + flag = Cproton::PN_CUMULATIVE + else + flag = 0 + end + Cproton.pn_messenger_settle(@impl, tracker.impl, flag) + end + + # Sets the incoming window. + # + # The Messenger will track the remote status of this many incoming + # deliveries after they have been accepted or rejected. + # + # Messages enter this window only when you take them into your application + # using get(). If your incoming window size is n, and you get n+1 messages + # without explicitly accepting or rejecting the oldest message, then the + # message that passes beyond the edge of the incoming window will be + # assigned the default disposition of its link. + # + # ==== Options + # + # * window - the window size + # + def incoming_window=(window) + raise TypeError.new("invalid window: #{window}") unless valid_window?(window) + Cproton.pn_messenger_set_incoming_window(@impl, window) + end + + # Returns the incoming window. + # + def incoming_window + Cproton.pn_messenger_get_incoming_window(@impl) + end + + # Sets the outgoing window. + # + # The Messenger will track the remote status of this many outgoing + # deliveries after calling send. + # A Message enters this window when you call the put() method with the + # message. If your outgoing window size is n, and you call put n+1 + # times, status information will no longer be available for the + # first message. + # + # ==== Options + # + # * window - the window size + # + def outgoing_window=(window) + raise TypeError.new("invalid window: #{window}") unless valid_window?(window) + Cproton.pn_messenger_set_outgoing_window(@impl, window) + end + + # Returns the outgoing window. + # + def outgoing_window + Cproton.pn_messenger_get_outgoing_window(@impl) + end + + # Unregisters a selectable object. + def unregister_selectable(fileno) # :nodoc: + @selectables.delete(fileno) + end + + private + + def valid_tracker?(tracker) + !tracker.nil? && tracker.is_a?(Qpid::Proton::Tracker) + end + + def valid_window?(window) + !window.nil? && [Float, Fixnum].include?(window.class) + end + + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/proton-c/bindings/ruby/lib/messenger/selectable.rb ---------------------------------------------------------------------- diff --git a/proton-c/bindings/ruby/lib/messenger/selectable.rb b/proton-c/bindings/ruby/lib/messenger/selectable.rb new file mode 100644 index 0000000..33554cd --- /dev/null +++ b/proton-c/bindings/ruby/lib/messenger/selectable.rb @@ -0,0 +1,126 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + # Selectable enables accessing the underlying file descriptors + # for Messenger. + class Selectable + + include Qpid::Proton::Filters + + call_before :check_is_initialized, + :fileno, :capacity, :pending, :deadline, + :readable, :writable, :expired, + :registered=, :registered? + + def initialize(messenger, impl) # :nodoc: + @messenger = messenger + @impl = impl + @io = nil + @freed = false + end + + # Returns the underlying file descriptor. + # + # This can be used in conjunction with the IO class. + # + def fileno + Cproton.pn_selectable_fd(@impl) + end + + def to_io + @io ||= IO.new(fileno) + end + + # The number of bytes the selectable is capable of consuming. + # + def capacity + Cproton.pn_selectable_capacity(@impl) + end + + # The number of bytes waiting to be written to the file descriptor. + # + def pending + Cproton.pn_selectable_pending(@impl) + end + + # The future expiry time at which control will be returned to the + # selectable. + # + def deadline + tstamp = Cproton.pn_selectable_deadline(@impl) + tstamp.nil? ? nil : tstamp / 1000 + end + + def readable + Cproton.pn_selectable_readable(@impl) + end + + def writable + Cproton.pn_selectable_writable(@impl) + end + + def expired? + Cproton.pn_selectable_expired(@impl) + end + + def registered=(registered) + Cproton.pn_selectable_set_registered(@impl, registered) + end + + def registered? + Cproton.pn_selectable_is_registered(@impl) + end + + def terminal? + return true if @impl.nil? + Cproton.pn_selectable_is_terminal(@impl) + end + + def to_s + "fileno=#{self.fileno} registered=#{self.registered?} terminal=#{self.terminal?}" + end + + def free + return if @freed + @freed = true + @messenger.unregister_selectable(fileno) + @io.close unless @io.nil? + Cproton.pn_selectable_free(@impl) + @impl = nil + end + + def freed? # :nodoc: + @freed + end + + private + + def check_is_initialized + raise RuntimeError.new("selectable freed") if @impl.nil? + end + + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/proton-c/bindings/ruby/lib/messenger/subscription.rb ---------------------------------------------------------------------- diff --git a/proton-c/bindings/ruby/lib/messenger/subscription.rb b/proton-c/bindings/ruby/lib/messenger/subscription.rb new file mode 100644 index 0000000..21d9281 --- /dev/null +++ b/proton-c/bindings/ruby/lib/messenger/subscription.rb @@ -0,0 +1,41 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + # A +Subscription+ is an opaque object for working with a +Messenger+'s + # subscriptions. + # + class Subscription + + def initialize(impl) # :nodoc: + @impl = impl + end + + def impl # :nodoc: + @impl + end + + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/proton-c/bindings/ruby/lib/messenger/tracker.rb ---------------------------------------------------------------------- diff --git a/proton-c/bindings/ruby/lib/messenger/tracker.rb b/proton-c/bindings/ruby/lib/messenger/tracker.rb new file mode 100644 index 0000000..7de271a --- /dev/null +++ b/proton-c/bindings/ruby/lib/messenger/tracker.rb @@ -0,0 +1,42 @@ +#-- +# 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 # :nodoc: + + module Proton # :nodoc: + + # A +Tracker+ is used to track the disposition of a +Message+. + # + class Tracker + + CUMULATIVE = Cproton::PN_CUMULATIVE + + def initialize(impl) # :nodoc: + @impl = impl + end + + def impl # :nodoc: + @impl + end + + end + + end + +end http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/46b91f78/proton-c/bindings/ruby/lib/messenger/tracker_status.rb ---------------------------------------------------------------------- diff --git a/proton-c/bindings/ruby/lib/messenger/tracker_status.rb b/proton-c/bindings/ruby/lib/messenger/tracker_status.rb new file mode 100644 index 0000000..81c9ea3 --- /dev/null +++ b/proton-c/bindings/ruby/lib/messenger/tracker_status.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. +#++ + +module Qpid # :nodoc: + + module Proton # :nodoc: + + # TrackerStatus contains symbols that represent the status value for a + # Tracker. + # + class TrackerStatus + + def initialize value, name # :nodoc: + @value = value + @name = name + end + + def value # :nodoc: + @value + end + + def to_s # :nodoc: + @name.to_s + end + + def self.by_name(name) # :nodoc: + @by_name[name.to_sym] unless name.nil? + end + + def self.by_value(value) # :nodoc: + @by_value[value] unless value.nil? + end + + private + + def self.add_item(key, value) # :nodoc: + @by_name ||= {} + @by_name[key] = TrackerStatus.new value, key + @by_value ||= {} + @by_value[value] = @by_name[key] + end + + def self.const_missing(key) # :nodoc: + @by_name[key] + end + + self.add_item :UNKNOWN, Cproton::PN_STATUS_UNKNOWN + self.add_item :PENDING, Cproton::PN_STATUS_PENDING + self.add_item :ACCEPTED, Cproton::PN_STATUS_ACCEPTED + self.add_item :REJECTED, Cproton::PN_STATUS_REJECTED + self.add_item :SETTLED, Cproton::PN_STATUS_SETTLED + + end + + end + +end --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org