diff options
author | Stephen D. Huston <shuston@apache.org> | 2011-10-21 14:42:12 +0000 |
---|---|---|
committer | Stephen D. Huston <shuston@apache.org> | 2011-10-21 14:42:12 +0000 |
commit | f83677056891e436bf5ba99e79240df2a44528cd (patch) | |
tree | 625bfd644b948e89105630759cf6decb0435354d /ruby/lib | |
parent | ebfd9ff053b04ab379acfc0fefedee5a31b6d8a5 (diff) | |
download | qpid-python-QPID-2519.tar.gz |
Merged out from trunkQPID-2519
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/QPID-2519@1187375 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'ruby/lib')
-rw-r--r-- | ruby/lib/qpid.rb | 41 | ||||
-rw-r--r-- | ruby/lib/qpid/assembler.rb | 148 | ||||
-rw-r--r-- | ruby/lib/qpid/client.rb | 136 | ||||
-rw-r--r-- | ruby/lib/qpid/codec.rb | 457 | ||||
-rw-r--r-- | ruby/lib/qpid/codec08.rb | 265 | ||||
-rw-r--r-- | ruby/lib/qpid/config.rb | 32 | ||||
-rw-r--r-- | ruby/lib/qpid/connection.rb | 222 | ||||
-rw-r--r-- | ruby/lib/qpid/connection08.rb | 252 | ||||
-rw-r--r-- | ruby/lib/qpid/datatypes.rb | 353 | ||||
-rw-r--r-- | ruby/lib/qpid/delegates.rb | 237 | ||||
-rw-r--r-- | ruby/lib/qpid/fields.rb | 49 | ||||
-rw-r--r-- | ruby/lib/qpid/framer.rb | 212 | ||||
-rw-r--r-- | ruby/lib/qpid/invoker.rb | 65 | ||||
-rw-r--r-- | ruby/lib/qpid/packer.rb | 33 | ||||
-rw-r--r-- | ruby/lib/qpid/peer.rb | 289 | ||||
-rw-r--r-- | ruby/lib/qpid/qmf.rb | 1957 | ||||
-rw-r--r-- | ruby/lib/qpid/queue.rb | 101 | ||||
-rw-r--r-- | ruby/lib/qpid/session.rb | 458 | ||||
-rw-r--r-- | ruby/lib/qpid/spec.rb | 183 | ||||
-rw-r--r-- | ruby/lib/qpid/spec010.rb | 485 | ||||
-rw-r--r-- | ruby/lib/qpid/spec08.rb | 190 | ||||
-rw-r--r-- | ruby/lib/qpid/specs/amqp.0-10-qpid-errata.xml | 6654 | ||||
-rw-r--r-- | ruby/lib/qpid/specs/amqp.0-10.dtd | 246 | ||||
-rw-r--r-- | ruby/lib/qpid/test.rb | 35 | ||||
-rw-r--r-- | ruby/lib/qpid/traverse.rb | 64 | ||||
-rw-r--r-- | ruby/lib/qpid/util.rb | 75 |
26 files changed, 0 insertions, 13239 deletions
diff --git a/ruby/lib/qpid.rb b/ruby/lib/qpid.rb deleted file mode 100644 index 1c719e9b1d..0000000000 --- a/ruby/lib/qpid.rb +++ /dev/null @@ -1,41 +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 - def self.logger - @logger ||= {} - @logger - end -end - -require "qpid/util" -require "qpid/queue" -require "qpid/packer" -require "qpid/framer" -require "qpid/codec" -require 'qpid/datatypes' -require 'qpid/spec010' -require 'qpid/delegates' -require 'qpid/invoker' -require "qpid/assembler" -require 'qpid/session' -require "qpid/connection" -require "qpid/spec" -require 'qpid/queue' -require 'qpid/qmf' diff --git a/ruby/lib/qpid/assembler.rb b/ruby/lib/qpid/assembler.rb deleted file mode 100644 index b768c3f195..0000000000 --- a/ruby/lib/qpid/assembler.rb +++ /dev/null @@ -1,148 +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 - - class << self - attr_accessor :asm_logger - end - - class Segment - - attr_reader :type, :payload, :track, :channel - attr_accessor :id, :offset - - def initialize(first, last, type, track, channel, payload) - @id = nil - @offset = nil - @first = first - @last = last - @type = type - @track = track - @channel = channel - @payload = payload - end - - def first_segment? ; @first ; end - - def last_segment? ; @last ; end - - def decode(spec) - segs = spec[:segment_type] - choice = segs.enum.choices[type] - return method("decode_#{choice.name}").call(spec) - end - - def decode_control(spec) - sc = StringCodec.new(spec, payload) - return sc.read_control() - end - - def decode_command(spec) - sc = StringCodec.new(spec, payload) - hdr, cmd = sc.read_command() - cmd.id = id - return hdr, cmd - end - - def decode_header(spec) - sc = StringCodec.new(spec, payload) - values = [] - until sc.encoded.empty? - values << sc.read_struct32() - end - return values - end - - def decode_body(spec) - payload - end - - def append(frame) - @payload += frame.payload - end - - def to_s - f = first_segment? ? 'F' : '.' - l = last_segment? ? 'L' : '.' - return "%s%s %s %s %s %s" % [f, l, @type, - @track, @channel, @payload.inspect] - end - - end - - class Assembler < Framer - - def logger; Qpid::asm_logger; end - - def initialize(sock, max_payload = Frame::MAX_PAYLOAD) - super(sock) - @max_payload = max_payload - @fragments = {} - end - - def read_segment - loop do - frame = read_frame - key = [frame.channel, frame.track] - seg = @fragments[key] - unless seg - seg = Segment.new(frame.first_segment?, - frame.last_segment?, - frame.type, frame.track, - frame.channel, "") - @fragments[key] = seg - end - - seg.append(frame) - - if frame.last_frame? - @fragments.delete(key) - logger.debug("RECV #{seg}") if logger - return seg - end - end - end - - def write_segment(segment) - remaining = segment.payload - - first = true - while first or remaining - payload = remaining[0, @max_payload] - remaining = remaining[@max_payload, remaining.size] - - flags = 0 - - flags |= FIRST_FRM if first - flags |= LAST_FRM unless remaining - flags |= FIRST_SEG if segment.first_segment? - flags |= LAST_SEG if segment.last_segment? - - frame = Frame.new(flags, segment.type, segment.track, - segment.channel, payload) - write_frame(frame) - - first = false - end - - logger.debug("SENT #{segment}") if logger - end - end -end diff --git a/ruby/lib/qpid/client.rb b/ruby/lib/qpid/client.rb deleted file mode 100644 index ec3d100a9c..0000000000 --- a/ruby/lib/qpid/client.rb +++ /dev/null @@ -1,136 +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 "thread" -require "qpid/peer" -require "qpid/queue" - -module Qpid08 - - class Client - def initialize(host, port, spec, vhost = "/") - @host = host - @port = port - @spec = spec - @vhost = vhost - - @mechanism = nil - @response = nil - @locale = nil - - @queues = {} - @mutex = Mutex.new() - - @closed = false - @code = nil - @started = ConditionVariable.new() - - @conn = Connection.new(@host, @port, @spec) - @peer = Peer.new(@conn, ClientDelegate.new(self)) - end - - attr_reader :mechanism, :response, :locale - - def closed?; @closed end - def closed=(value); @closed = value end - def code; @code end - - def wait() - @mutex.synchronize do - @started.wait(@mutex) - end - raise EOFError.new() if closed? - end - - def signal_start() - @started.broadcast() - end - - def queue(key) - @mutex.synchronize do - q = @queues[key] - if q.nil? - q = Queue.new() - @queues[key] = q - end - return q - end - end - - def start(response, mechanism="AMQPLAIN", locale="en_US") - @response = response - @mechanism = mechanism - @locale = locale - - @conn.connect() - @conn.init() - @peer.start() - wait() - channel(0).connection_open(@vhost) - end - - def channel(id) - return @peer.channel(id) - end - - def close(msg = nil) - @closed = true - @code = msg - @peer.close() - end - end - - class ClientDelegate - - include Delegate - - def initialize(client) - @client = client - end - - def connection_start(ch, msg) - ch.connection_start_ok(:mechanism => @client.mechanism, - :response => @client.response, - :locale => @client.locale) - end - - def connection_tune(ch, msg) - ch.connection_tune_ok(*msg.fields) - @client.signal_start() - end - - def connection_close(ch, msg) - puts "CONNECTION CLOSED: #{msg.args.join(", ")}" - @client.close(msg) - end - - def channel_close(ch, msg) - puts "CHANNEL[#{ch.id}] CLOSED: #{msg.args.join(", ")}" - ch.channel_close_ok() - ch.close() - end - - def basic_deliver(ch, msg) - queue = @client.queue(msg.consumer_tag) - queue << msg - end - - end - -end diff --git a/ruby/lib/qpid/codec.rb b/ruby/lib/qpid/codec.rb deleted file mode 100644 index a3b5d101c4..0000000000 --- a/ruby/lib/qpid/codec.rb +++ /dev/null @@ -1,457 +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/packer.rb' -require 'iconv' - -module Qpid - - class Codec - - include Qpid::Packer - - attr_reader :spec - - def initialize(spec = "") - @spec = spec - end - - def write_void(v) - unless v.nil? - raise Exception.new("void not nil: #{v}") - end - end - - def read_void - return nil - end - - def write_bit(b) - unless b - raise Exception.new("bit is nil: #{b}") - end - end - - def read_bit - return true - end - - def read_uint8 - return unpack("C", 1) - end - - def write_uint8(n) - return pack("C", n) - end - - def read_int8 - return unpack("c", 1) - end - - def write_int8(n) - pack("c", n) - end - - def read_char - return unpack("c", 1) - end - - def write_char(c) - pack("c") - end - - def read_boolean - return read_uint8 != 0 - end - - def write_boolean(b) - n = 0 - n = 1 if b != 0 - write_uint8(n) - end - - def read_uint16 - return unpack("n", 2) - end - - def write_uint16(n) - pack("n", n) - end - - def read_int16 - # XXX: holy moly.. pack/unpack doesn't have signed network byte order. Crazy hackery. - val = unpack("n", 2) - val -= 2 ** 16 if val >= 2 ** 15 - return val - end - - def write_int16(n) - # XXX: Magically this one works even though it's not signed. - pack("n", n) - end - - def read_uint32 - return unpack("N", 4) - end - - def write_uint32(n) - pack("N", n) - end - - def read_int32 - # Again no pack/unpack for signed int - return unpack("N", 4) - end - - def write_int32(n) - # FIXME - pack("N", n) - end - - def read_float - return unpack("g", 4) - end - - def write_float(n) - pack("g", n) - end - - def read_sequence_no - return read_uint32.to_serial - end - - def write_sequence_no(n) - write_uint32(n.value) - end - - def encode_64bit(num, signed = false) - b = [] - - if num < 0 && signed - num += 2 ** 64 - end - - (0..7).each do |c| - d = 7 - c - b[c] = (num & (0xff << d * 8)) >> d * 8 - end - pack('C8', *b) - end - - - def decode_64bit(signed = false) - # Silly ruby pack/unpack does not implement 64 bit network byte order - # encode/decode. - a = unpack('C8', 8) - num = 0 - (0..7).each do |c| - d = 7 - c - num |= a[c] << 8 * d - end - - if signed && num >= 2 ** 63 - num -= 2 ** 64 - end - return num - end - - def read_uint64 - return decode_64bit - end - - def write_uint64(n) - encode_64bit(n) - end - - def read_int64 - return decode_64bit(signed = true) - end - - def write_int64(n) - encode_64bit(n, signed = true) - end - - def read_datetime - return read_uint64 - end - - def write_datetime(n) - write_uint64(n) - end - - def read_double - return unpack("G", 8) - end - - def write_double(n) - pack("G", n) - end - - def read_vbin8 - # XXX - return read(read_uint8) - end - - def write_vbin8(b) - # XXX - write_uint8(b.length) - write(b) - end - - def read_str8 - # FIXME: Check iconv.. I think this will throw if there are odd characters. - return Iconv.conv("ASCII", "UTF-8", read_vbin8) - end - - def write_str8(s) - write_vbin8(Iconv.conv("UTF-8", "ASCII", s)) - end - - def read_str16 - return Iconv.conv("ASCII", "UTF-8", read_vbin16) - end - - def write_str16(s) - write_vbin16(Iconv.conv("UTF-8", "ASCII", s)) - end - - def read_vbin16 - # XXX: Using read method? - return read(read_uint16) - end - - def write_vbin16(b) - write_uint16(b.length) - write(b) - end - - def read_sequence_set - # FIXME: Need datatypes - result = RangedSet.new - size = read_uint16 - nranges = size / 8 - nranges.times do |i| - lower = read_sequence_no - upper = read_sequence_no - result.add(lower, upper) - end - return result - end - - def write_sequence_set(ss) - size = 8 * ss.ranges.length - write_uint16(size) - ss.ranges.each do |range| - write_sequence_no(range.lower) - write_sequence_no(range.upper) - end - end - - def read_vbin32 - return read(read_uint32) - end - - def write_vbin32(b) - write_uint32(b.length) - write(b) - end - - def write_map(m) - sc = StringCodec.new(@spec) - unless m.nil? - sc.write_uint32(m.size) - m.each do |k, v| - unless type = @spec.encoding(v.class) - raise Exception.new("no encoding for: #{v.class}") - end - sc.write_str8(k) - sc.write_uint8(type.code) - type.encode(sc, v) - end - end - write_vbin32(sc.encoded) - end - - def read_map - sc = StringCodec.new(@spec, read_vbin32) - return nil unless sc.encoded - count = sc.read_uint32 - result = nil - if count - result = {} - until sc.encoded.empty? - k = sc.read_str8 - code = sc.read_uint8 - type = @spec.types[code] - v = type.decode(sc) - result[k] = v - end - end - return result - end - - def write_array(a) - sc = StringCodec.new(@spec) - unless a.nil? - if a.length > 0 - type = @spec.encoding(a[0].class) - else - type = @spec.encoding(nil.class) - end - sc.write_uint8(type.code) - sc.write_uint32(a.size) - a.each { |o| type.encode(sc, o) } - end - write_vbin32(sc.encoded) - end - - def read_array - sc = StringCodec.new(@spec, read_vbin32) - return nil if not sc.encoded - type = @spec.types[sc.read_uint8] - count = sc.read_uint32 - result = nil - if count - result = [] - count.times { |i| result << (type.decode(sc)) } - end - return result - end - - def write_list(l) - sc = StringCodec.new(@spec) - unless l.nil? - sc.write_uint32(l.length) - l.each do |o| - type = @spec.encoding(o.class) - sc.write_uint8(type.code) - type.encode(sc, o) - end - end - write_vbin32(sc.encoded) - end - - def read_list - sc = StringCodec.new(@spec, read_vbin32) - return nil if not sc.encoded - count = sc.read_uint32 - result = nil - if count - result = [] - count.times do |i| - type = @spec.types[sc.read_uint8] - result << type.decode(sc) - end - end - return result - end - - def read_struct32 - size = read_uint32 - code = read_uint16 - type = @spec.structs[code] - # XXX: BLEH! - fields = type.decode_fields(self) - return Qpid::struct(type, fields) - end - - def write_struct32(value) - type = value.st_type - sc = StringCodec.new(@spec) - sc.write_uint16(type.code) - type.encode_fields(sc, value) - write_vbin32(sc.encoded) - end - - def read_control - cntrl = @spec.controls[read_uint16] - return Qpid::struct(cntrl, cntrl.decode_fields(self)) - end - - def write_control(ctrl) - type = ctrl.st_type - write_uint16(type.code) - type.encode_fields(self, ctrl) - end - - def read_command - type = @spec.commands[read_uint16] - hdr = @spec[:header].decode(self) - cmd = Qpid::struct(type, type.decode_fields(self)) - return hdr, cmd - end - - def write_command(hdr, cmd) - type = cmd.st_type - write_uint16(type.code) - hdr.st_type.encode(self, hdr) - type.encode_fields(self, cmd) - end - - def read_size(width) - if width > 0 - return send(:"read_uint#{width * 8}") - end - end - - def write_size(width, n) - if width > 0 - send(:"write_uint#{width * 8}", n) - end - end - - def read_uuid - return unpack("a16", 16) - end - - def write_uuid(s) - pack("a16", s) - end - - def read_bin128 - return unpack("a16", 16) - end - - def write_bin128(b) - pack("a16", b) - end - - end - - class StringCodec < Codec - - def initialize(spec, encoded = "") - @spec = spec - @encoded = encoded - end - - attr_reader :encoded - - def write(s) - @encoded += s - end - - def read(n) - return "" if n.nil? - result = @encoded[0...n] - @encoded = @encoded[n...@encoded.size] || "" - return result - end - end -end diff --git a/ruby/lib/qpid/codec08.rb b/ruby/lib/qpid/codec08.rb deleted file mode 100644 index 148dee07bb..0000000000 --- a/ruby/lib/qpid/codec08.rb +++ /dev/null @@ -1,265 +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 Qpid08 - # is there a better way to do this? - class StringWriter - - def initialize(str = "") - @str = str - end - - def write(value) - @str << value - end - - def to_s() - return @str - end - - end - - class EOF < Exception; end - - class Encoder - - def initialize(out) - @out = out - @bits = [] - end - - attr_reader(:out) - - def encode(type, value) - send(type, value) - end - - def bit(b) - @bits << b - end - - def octet(o) - pack("C", o) - end - - def short(s) - pack("n", s) - end - - def long(l) - pack("N", l) - end - - def longlong(l) - lower = l & 0xffffffff - upper = (l & ~0xffffffff) >> 32 - long(upper) - long(lower) - end - - def timestamp(l) - longlong(l) - end - - def shortstr(s) - # shortstr is actually octetstr - octet(s.length) - write(s) - end - - def longstr(s) - case s - when Hash - table(s) - else - long(s.length) - write(s) - end - end - - def table(t) - t = {} if t.nil? - enc = Encoder.new(StringWriter.new()) - t.each {|key, value| - enc.shortstr(key) - # I offer this chicken to the gods of polymorphism. May they - # choke on it. - case value - when String - type = :longstr - desc = "S" - when Numeric - type = :long - desc = "I" - else - raise Exception.new("unknown table value: #{value.class}") - end - enc.write(desc) - enc.encode(type, value) - } - longstr(enc.out.to_s()) - end - - def write(str) - flushbits() - @out.write(str) - # puts "OUT #{str.inspect()}" - end - - def pack(fmt, *args) - write(args.pack(fmt)) - end - - def flush() - flushbits() - end - - private - - def flushbits() - if @bits.empty? then return end - - bytes = [] - index = 0 - @bits.each {|b| - bytes << 0 if index == 0 - if b then bytes[-1] |= 1 << index end - index = (index + 1) % 8 - } - @bits.clear() - bytes.each {|b| - octet(b) - } - end - - end - - class StringReader - - def initialize(str) - @str = str - @index = 0 - end - - def read(n) - result = @str[@index, n] - @index += result.length - return result - end - - end - - class Decoder - - def initialize(_in) - @in = _in - @bits = [] - end - - def decode(type) - return send(type) - end - - def bit() - if @bits.empty? - byte = octet() - 7.downto(0) {|i| - @bits << (byte[i] == 1) - } - end - return @bits.pop() - end - - def octet() - return unpack("C", 1) - end - - def short() - return unpack("n", 2) - end - - def long() - return unpack("N", 4) - end - - def longlong() - upper = long() - lower = long() - return upper << 32 | lower - end - - def timestamp() - return longlong() - end - - def shortstr() - # shortstr is actually octetstr - return read(octet()) - end - - def longstr() - return read(long()) - end - - def table() - dec = Decoder.new(StringReader.new(longstr())) - result = {} - while true - begin - key = dec.shortstr() - rescue EOF - break - end - desc = dec.read(1) - case desc - when "S" - value = dec.longstr() - when "I" - value = dec.long() - else - raise Exception.new("unrecognized descriminator: #{desc.inspect()}") - end - result[key] = value - end - return result - end - - def read(n) - return "" if n == 0 - result = @in.read(n) - if result.nil? or result.empty? - raise EOF.new() - else - # puts " IN #{result.inspect()}" - return result - end - end - - def unpack(fmt, size) - result = read(size).unpack(fmt) - if result.length == 1 - return result[0] - else - return result - end - end - - end - -end diff --git a/ruby/lib/qpid/config.rb b/ruby/lib/qpid/config.rb deleted file mode 100644 index b5b79cd309..0000000000 --- a/ruby/lib/qpid/config.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 - module Config - - def self.amqp_spec - dirs = [File::expand_path(File::join(File::dirname(__FILE__), "specs"))] - dirs.each do |d| - spec = File::join(d, "amqp.0-10-qpid-errata.xml") - return spec if File::exists? spec - end - end - - end -end diff --git a/ruby/lib/qpid/connection.rb b/ruby/lib/qpid/connection.rb deleted file mode 100644 index d2efbfb263..0000000000 --- a/ruby/lib/qpid/connection.rb +++ /dev/null @@ -1,222 +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 'monitor' - -module Qpid - - class ChannelBusy< Exception ; end - - class ChannelsBusy < Exception ; end - - class SessionBusy < Exception ; end - - class ConnectionFailed < Exception ; end - - class Timeout < Exception ; end - - class Connection < Assembler - - include MonitorMixin - - attr_reader :spec, :attached, :sessions, :thread - attr_accessor :opened, :failed, :close_code, :user_id - - def initialize(sock, args={}) - super(sock) - - delegate = args[:delegate] || Qpid::Delegate::Client.method(:new) - spec = args[:spec] || nil - - @spec = Qpid::Spec010::load(spec) - @track = @spec["track"] - - @attached = {} - @sessions = {} - - @condition = new_cond - @opened = false - @failed = false - @close_code = [nil, "connection aborted"] - - @thread = nil - - @channel_max = 65535 - @user_id = nil - - @delegate = delegate.call(self, args) - end - - def attach(name, ch, delegate, force=false) - synchronize do - ssn = @attached[ch.id] - if ssn - raise ChannelBusy.new(ch, ssn) unless ssn.name == name - else - ssn = @sessions[name] - if ssn.nil? - ssn = Session.new(name, @spec, :delegate => delegate) - @sessions[name] = ssn - elsif ssn.channel - if force - @attached.delete(ssn.channel.id) - ssn.channel = nil - else - raise SessionBusy.new(ssn) - end - end - @attached[ch.id] = ssn - ssn.channel = ch - end - ch.session = ssn - return ssn - end - end - - def detach(name, ch) - synchronize do - @attached.delete(ch.id) - ssn = @sessions.delete(name) - if ssn - ssn.channel = nil - ssn.closed - return ssn - end - end - end - - def session(name, kwargs = {}) - timeout = kwargs[:timeout] - delegate = kwargs[:delegate] || Qpid::Session::Client.method(:new) - - # FIXME: Python has cryptic comment about 'ch 0 ?' - channel = (0..@channel_max).detect { |i| ! @attached.key?(i) } - raise ChannelsBusy unless channel - - synchronize do - ch = Channel.new(self, channel) - ssn = attach(name, ch, delegate) - ssn.channel.session_attach(name) - if ssn.wait_for(timeout) { ssn.channel } - return ssn - else - detach(name, ch) - raise Timeout - end - end - end - - def detach_all - synchronize do - attached.values.each do |ssn| - ssn.exceptions << @close_code unless @close_code[0] == 200 - detach(ssn.name, ssn.channel) - end - end - end - - def start(timeout=nil) - @delegate.start - @thread = Thread.new { run } - @thread[:name] = 'conn' - synchronize do - unless @condition.wait_for(timeout) { @opened || @failed } - raise Timeout - end - end - if @failed - raise ConnectionFailed.new(@close_code) - end - end - - def run - # XXX: we don't really have a good way to exit this loop without - # getting the other end to kill the socket - loop do - begin - seg = read_segment - rescue Qpid::Closed => e - detach_all - break - end - @delegate.received(seg) - end - end - - def close(timeout=nil) - return unless @opened - Channel.new(self, 0).connection_close(200) - synchronize do - unless @condition.wait_for(timeout) { ! @opened } - raise Timeout - end - end - @thread.join(timeout) - @thread = nil - end - - def signal - synchronize { @condition.signal } - end - - def to_s - # FIXME: We'd like to report something like HOST:PORT - return @sock.to_s - end - - class Channel < Invoker - - attr_reader :id, :connection - attr_accessor :session - - def initialize(connection, id) - @connection = connection - @id = id - @session = nil - end - - def resolve_method(name) - inst = @connection.spec[name] - if inst.is_a?(Qpid::Spec010::Control) - return invocation(:method, inst) - else - return invocation(:error, nil) - end - end - - def invoke(type, args) - ctl = type.create(*args) - sc = StringCodec.new(@connection.spec) - sc.write_control(ctl) - @connection.write_segment(Segment.new(true, true, type.segment_type, - type.track, self.id, sc.encoded)) - - log = Qpid::logger["qpid.io.ctl"] - log.debug("SENT %s", ctl) if log - end - - def to_s - return "#{@connection}[#{@id}]" - end - - end - - end - -end diff --git a/ruby/lib/qpid/connection08.rb b/ruby/lib/qpid/connection08.rb deleted file mode 100644 index 09a4888cc4..0000000000 --- a/ruby/lib/qpid/connection08.rb +++ /dev/null @@ -1,252 +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 "socket" -require "qpid/codec08" - -module Qpid08 - - class Connection - - def initialize(host, port, spec) - @host = host - @port = port - @spec = spec - end - - attr_reader(:host, :port, :spec) - - def connect() - @sock = TCPSocket.open(@host, @port) - @out = Encoder.new(@sock) - @in = Decoder.new(@sock) - end - - def init() - @out.write("AMQP") - [1, 1, @spec.major, @spec.minor].each {|o| - @out.octet(o) - } - end - - def write(frame) - # puts "OUT #{frame.inspect()}" - @out.octet(@spec.constants[frame.payload.type].id) - @out.short(frame.channel) - frame.payload.encode(@out) - @out.octet(frame_end) - end - - def read() - type = @spec.constants[@in.octet()].name - channel = @in.short() - payload = Payload.decode(type, @spec, @in) - oct = @in.octet() - if oct != frame_end - raise Exception.new("framing error: expected #{frame_end}, got #{oct}") - end - frame = Frame.new(channel, payload) - # puts " IN #{frame.inspect}" - return frame - end - - private - - def frame_end - @spec.constants[:"frame_end"].id - end - - end - - class Frame - - def initialize(channel, payload) - @channel = channel - @payload = payload - end - - attr_reader(:channel, :payload) - - end - - class Payload - - TYPES = {} - - def Payload.singleton_method_added(name) - if name == :type - TYPES[type] = self - end - end - - def Payload.decode(type, spec, dec) - klass = TYPES[type] - klass.decode(spec, dec) - end - - end - - class Method < Payload - - def initialize(method, args) - if args.size != method.fields.size - raise ArgumentError.new("argument mismatch #{method} #{args}") - end - @method = method - @args = args - end - - attr_reader(:method, :args) - - def Method.type; :frame_method end - - def type; Method.type end - - def encode(encoder) - buf = StringWriter.new() - enc = Encoder.new(buf) - enc.short(@method.parent.id) - enc.short(@method.id) - @method.fields.zip(self.args).each {|f, a| - if a.nil?; a = f.default end - enc.encode(f.type, a) - } - enc.flush() - encoder.longstr(buf.to_s) - end - - def Method.decode(spec, decoder) - buf = decoder.longstr() - dec = Decoder.new(StringReader.new(buf)) - klass = spec.classes[dec.short()] - meth = klass.methods[dec.short()] - args = meth.fields.map {|f| dec.decode(f.type)} - return Method.new(meth, args) - end - - def inspect(); "#{method.qname}(#{args.join(", ")})" end - - end - - class Header < Payload - - def Header.type; :frame_header end - - def initialize(klass, weight, size, properties) - @klass = klass - @weight = weight - @size = size - @properties = properties - end - - attr_reader :weight, :size, :properties - - def type; Header.type end - - def encode(encoder) - buf = StringWriter.new() - enc = Encoder.new(buf) - enc.short(@klass.id) - enc.short(@weight) - enc.longlong(@size) - - # property flags - nprops = @klass.fields.size - flags = 0 - 0.upto(nprops - 1) do |i| - f = @klass.fields[i] - flags <<= 1 - flags |= 1 unless @properties[f.name].nil? - # the last bit indicates more flags - if i > 0 and (i % 15) == 0 - flags <<= 1 - if nprops > (i + 1) - flags |= 1 - enc.short(flags) - flags = 0 - end - end - end - flags <<= ((16 - (nprops % 15)) % 16) - enc.short(flags) - - # properties - @klass.fields.each do |f| - v = @properties[f.name] - enc.encode(f.type, v) unless v.nil? - end - enc.flush() - encoder.longstr(buf.to_s) - end - - def Header.decode(spec, decoder) - dec = Decoder.new(StringReader.new(decoder.longstr())) - klass = spec.classes[dec.short()] - weight = dec.short() - size = dec.longlong() - - # property flags - bits = [] - while true - flags = dec.short() - 15.downto(1) do |i| - if flags >> i & 0x1 != 0 - bits << true - else - bits << false - end - end - break if flags & 0x1 == 0 - end - - # properties - properties = {} - bits.zip(klass.fields).each do |b, f| - properties[f.name] = dec.decode(f.type) if b - end - return Header.new(klass, weight, size, properties) - end - - def inspect(); "#{@klass.name}(#{@properties.inspect()})" end - - end - - class Body < Payload - - def Body.type; :frame_body end - - def type; Body.type end - - def initialize(content) - @content = content - end - - attr_reader :content - - def encode(enc) - enc.longstr(@content) - end - - def Body.decode(spec, dec) - return Body.new(dec.longstr()) - end - - end - -end diff --git a/ruby/lib/qpid/datatypes.rb b/ruby/lib/qpid/datatypes.rb deleted file mode 100644 index 418388c73a..0000000000 --- a/ruby/lib/qpid/datatypes.rb +++ /dev/null @@ -1,353 +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 - - def self.struct(type, *args) - # FIXME: This is fragile; the last arg could be a hash, - # without being hte keywords - kwargs = {} - kwargs = args.pop if args.any? && args[-1].is_a?(Hash) - - if args.size > type.fields.size - raise TypeError, - "%s() takes at most %d arguments (%d given)" % - [type.name, type.fields.size, args.size] - end - - attrs = type.fields.inject({}) do |attrs, field| - if args.any? - attrs[field.name] = args.shift - if kwargs.key?(field.name) - raise TypeError, - "%s() got multiple values for keyword argument '%s'" % - [type.name, field.name] - end - elsif kwargs.key?(field.name) - attrs[field.name] = kwargs.delete(field.name) - else - attrs[field.name] = field.default - end - attrs - end - - unless kwargs.empty? - unexpected = kwargs.keys[0] - raise TypeError, - "%s() got an unexpected keyword argument '%s'" % - [type.name, unexpected] - end - - attrs[:st_type] = type - attrs[:id] = nil - - name = "Qpid_" + type.name.to_s.capitalize - unless ::Struct.const_defined?(name) - vars = type.fields.collect { |f| f.name } << :st_type << :id - ::Struct.new(name, *vars) - end - st = ::Struct.const_get(name) - - result = st.new - attrs.each { |k, v| result[k] = v } - return result - end - - class Message - - attr_accessor :headers, :body, :id - - def initialize(*args) - @body = nil - @headers = nil - - @body = args.pop unless args.empty? - @headers = args unless args.empty? - - @id = nil - end - - def has(name) - return ! get(name).nil? - end - - def get(name) - if @headers - name = name.to_sym - @headers.find { |h| h.st_type.name == name } - end - end - - def set(header) - @headers ||= [] - if h = @headers.find { |h| h.st_type == header.st_type } - ind = @headers.index(h) - @headers[ind] = header - else - @headers << header - end - end - - def clear(name) - if @headers - name = name.to_sym - @headers.delete_if { |h| h.st_type.name == name } - end - end - - # FIXME: Not sure what to do here - # Ruby doesn't have a notion of a evaluable string representation - # def __repr__(self): - # args = [] - # if self.headers: - # args.extend(map(repr, self.headers)) - # if self.body: - # args.append(repr(self.body)) - # if self.id is not None: - # args.append("id=%s" % self.id) - # return "Message(%s)" % ", ".join(args) - # end - end - - class ::Object - - def to_serial - Qpid::Serial.new(self) - end - end - - class Serial - - include Comparable - - attr_accessor :value - - def initialize(value) - @value = value & 0xFFFFFFFF - end - - def hash - @value.hash - end - - def to_serial - self - end - - def eql?(other) - other = other.to_serial - value.eql?(other.value) - end - - def <=>(other) - return 1 if other.nil? - - other = other.to_serial - - delta = (value - other.value) & 0xFFFFFFFF - neg = delta & 0x80000000 - mag = delta & 0x7FFFFFFF - - return (neg>0) ? -mag : mag - end - - def +(other) - result = other.to_serial - result.value += value - return result - end - - def -(other) - result = other.to_serial - result.value = value - result.value - return result - end - - def succ - Serial.new(value + 1) - end - - # FIXME: Not sure what to do here - # Ruby doesn't have a notion of a evaluable string representation - # def __repr__(self): - # return "serial(%s)" % self.value - # end - - def to_s - value.to_s - end - - end - - # The Python class datatypes.Range is emulated by the standard - # Range class with a few additions - class ::Range - - alias :lower :begin - alias :upper :end - - def touches(r) - # XXX: are we doing more checks than we need? - return (r.include?(lower - 1) || - r.include?(upper + 1) || - include?(r.lower - 1) || - include?(r.upper + 1) || - r.include?(lower) || - r.include?(upper) || - include?(r.lower) || - include?(r.upper)) - end - - def span(r) - Range.new([lower, r.lower].min, [upper, r.upper].max) - end - - def intersect(r) - l = [lower, r.lower].max - u = [upper, r.upper].min - return l > u ? nil : Range.new(l, u) - end - - end - - class RangedSet - - include Enumerable - - attr_accessor :ranges - - def initialize(*args) - @ranges = [] - args.each { |n| add(n) } - end - - def each(&block) - ranges.each { |r| yield(r) } - end - - def include?(n) - if (n.is_a?(Range)) - super(n) - else - ranges.find { |r| r.include?(n) } - end - end - - def add_range(range) - ranges.delete_if do |r| - if range.touches(r) - range = range.span(r) - true - else - false - end - end - ranges << range - end - - def add(lower, upper = nil) - upper = lower if upper.nil? - add_range(Range.new(lower, upper)) - end - - def to_s - repr = ranges.sort { |a,b| b.lower <=> a.lower }. - map { |r| r.to_s }.join(",") - "<RangedSet: {#{repr}}" - end - end - - class Future - def initialize(initial=nil, exception=Exception) - @value = initial - @error = nil - @set = Util::Event.new - @exception = exception - end - - def error(error) - @error = error - @set.set - end - - def set(value) - @value = value - @set.set - end - - def get(timeout=nil) - @set.wait(timeout) - unless @error.nil? - raise @exception.new(@error) - end - @value - end - end - - class UUID - include Comparable - - attr_accessor :bytes - - def initialize(bytes) - @bytes = bytes - end - - def <=>(other) - if other.respond_to?(:bytes) - return bytes <=> other.bytes - else - raise NotImplementedError - end - end - - def to_s - UUID::format(bytes) - end - - # FIXME: Not sure what to do here - # Ruby doesn't have a notion of a evaluable string representation - # def __repr__(self): - # return "UUID(%r)" % str(self) - # end - - def self.random_uuid - bytes = (1..16).collect { |i| rand(256) } - - # From RFC4122, the version bits are set to 0100 - bytes[7] &= 0x0F - bytes[7] |= 0x40 - - # From RFC4122, the top two bits of byte 8 get set to 01 - bytes[8] &= 0x3F - bytes[8] |= 0x80 - return bytes.pack("C16") - end - - def self.uuid4 - UUID.new(random_uuid) - end - - def self.format(s) - # Python format !LHHHHL - # big-endian, ulong, ushort x 4, ulong - "%08x-%04x-%04x-%04x-%04x%08x" % s.unpack("NnnnnN") - end - end -end diff --git a/ruby/lib/qpid/delegates.rb b/ruby/lib/qpid/delegates.rb deleted file mode 100644 index f779047e05..0000000000 --- a/ruby/lib/qpid/delegates.rb +++ /dev/null @@ -1,237 +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 'rbconfig' -require 'sasl' - -module Qpid - - class Delegate - - def initialize(connection, args={}) - @connection = connection - @spec = connection.spec - @delegate = args[:delegate] || Qpid::Delegate::Client.method(:new) - @control = @spec[:track].enum[:control].value - end - - def log ; Qpid::logger["qpid.io.ctl"]; end - - def received(seg) - ssn = @connection.attached[seg.channel] - unless ssn - ch = Qpid::Connection::Channel.new(@connection, seg.channel) - else - ch = ssn.channel - end - - if seg.track == @control - ctl = seg.decode(@spec) - log.debug("RECV %s", ctl) if log - attr = ctl.st_type.name - method(attr).call(ch, ctl) - elsif ssn.nil? - ch.session_detached - else - ssn.received(seg) - end - end - - def connection_close(ch, close) - @connection.close_code = [close.reply_code, close.reply_text] - ch.connection_close_ok - @connection.sock.close_write() - unless @connection.opened - @connection.failed = true - @connection.signal - end - end - - def connection_close_ok(ch, close_ok) - @connection.opened = false - @connection.signal - end - - def session_attach(ch, a) - begin - @connection.attach(a.name, ch, @delegate, a.force) - ch.session_attached(a.name) - rescue Qpid::ChannelBusy - ch.session_detached(a.name) - rescue Qpid::SessionBusy - ch.session_detached(a.name) - end - end - - def session_attached(ch, a) - ch.session.signal - end - - def session_detach(ch, d) - #send back the confirmation of detachment before removing the - #channel from the attached set; this avoids needing to hold the - #connection lock during the sending of this control and ensures - #that if the channel is immediately reused for a new session the - #attach request will follow the detached notification. - ch.session_detached(d.name) - ssn = @connection.detach(d.name, ch) - end - - def session_detached(ch, d) - @connection.detach(d.name, ch) - end - - def session_request_timeout(ch, rt) - ch.session_timeout(rt.timeout) - end - - def session_command_point(ch, cp) - ssn = ch.session - ssn.receiver.next_id = cp.command_id - ssn.receiver.next_offset = cp.command_offset - end - - def session_completed(ch, cmp) - ch.session.sender.has_completed(cmp.commands) - if cmp.timely_reply - ch.session_known_completed(cmp.commands) - end - ch.session.signal - end - - def session_known_completed(ch, kn_cmp) - ch.session.receiver.known_completed(kn_cmp.commands) - end - - def session_flush(ch, f) - rcv = ch.session.receiver - if f.expected - if rcv.next_id - exp = Qpid::RangedSet.new(rcv.next_id) - else - exp = nil - end - ch.session_expected(exp) - end - if f.confirmed - ch.session_confirmed(rcv.completed) - end - if f.completed - ch.session_completed(rcv.completed) - end - end - - class Server < Delegate - - def start - @connection.read_header() - @connection.write_header(@spec.major, @spec.minor) - ch = Qpid::Connection::Channel.new(@connection, 0) - ch.connection_start(:mechanisms => ["ANONYMOUS"]) - ch - end - - def connection_start_ok(ch, start_ok) - ch.connection_tune(:channel_max => 65535) - end - - def connection_tune_ok(ch, tune_ok) - nil - end - - def connection_open(ch, open) - @connection.opened = true - ch.connection_open_ok() - @connection.signal - end - end - - class Client < Delegate - - # FIXME: Python uses os.name for platform - we don't have an exact - # analog in Ruby - PROPERTIES = {"product" => "qpid python client", - "version" => "development", - "platform" => Config::CONFIG["build_os"], - "qpid.client_process" => File.basename($0), - "qpid.client_pid" => Process.pid, - "qpid.client_ppid" => Process.ppid} - - - def initialize(connection, args) - super(connection) - - result = Sasl::client_init - - @mechanism= args[:mechanism] - @username = args[:username] - @password = args[:password] - @service = args[:service] || "qpidd" - @min_ssf = args[:min_ssf] || 0 - @max_ssf = args[:max_ssf] || 65535 - - @saslConn = Sasl.client_new(@mechanism, @service, args[:host], - @username, @password, @min_ssf, @max_ssf) - end - - def start - @connection.write_header(@spec.major, @spec.minor) - @connection.read_header - end - - def connection_start(ch, start) - mech_list = "" - start.mechanisms.each do |m| - mech_list += m + " " - end - begin - resp = Sasl.client_start(@saslConn, mech_list) - @connection.user_id = Sasl.user_id(@saslConn) - ch.connection_start_ok(:client_properties => PROPERTIES, - :mechanism => resp[2], - :response => resp[1]) - rescue exception - ch.connection_close(:message => $!.message) - @connection.failed = true - @connection.signal - end - end - - def connection_secure(ch, secure) - resp = Sasl.client_step(@saslConn, secure.challenge) - @connection.user_id = Sasl.user_id(@saslConn) - ch.connection_secure_ok(:response => resp[1]) - end - - def connection_tune(ch, tune) - ch.connection_tune_ok(:channel_max => tune.channel_max, - :max_frame_size => tune.max_frame_size, - :heartbeat => 0) - ch.connection_open() - @connection.security_layer_tx = @saslConn - end - - def connection_open_ok(ch, open_ok) - @connection.security_layer_rx = @saslConn - @connection.opened = true - @connection.signal - end - end - end -end diff --git a/ruby/lib/qpid/fields.rb b/ruby/lib/qpid/fields.rb deleted file mode 100644 index cc87d07529..0000000000 --- a/ruby/lib/qpid/fields.rb +++ /dev/null @@ -1,49 +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 Class - def fields(*fields) - module_eval { - def initialize(*args, &block) - args = init_fields(*args) - - if respond_to? :init - init(*args) {|*a| yield(*a)} - elsif args.any? - raise ArgumentError, "extra arguments: #{args.inspect}" - end - end - } - - vars = fields.map {|f| :"@#{f.to_s().chomp("?")}"} - - define_method(:init_fields) {|*args| - vars.each {|v| - instance_variable_set(v, args.shift()) - } - args - } - - vars.each_index {|i| - define_method(fields[i]) { - instance_variable_get(vars[i]) - } - } - end -end diff --git a/ruby/lib/qpid/framer.rb b/ruby/lib/qpid/framer.rb deleted file mode 100644 index d057605383..0000000000 --- a/ruby/lib/qpid/framer.rb +++ /dev/null @@ -1,212 +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 'monitor' -require 'logger' -require 'sasl' - -module Qpid - - FIRST_SEG = 0x08 - LAST_SEG = 0x04 - FIRST_FRM = 0x02 - LAST_FRM = 0x01 - - class << self - attr_accessor :raw_logger, :frm_logger - end - - def self.packed_size(format) - # FIXME: This is a total copout to simulate Python's - # struct.calcsize - ([0]*256).pack(format).size - end - - class Frame - attr_reader :payload, :track, :flags, :type, :channel - - # HEADER = "!2BHxBH4x" - # Python Meaning Ruby - # ! big endian (implied by format char) - # 2B 2 uchar C2 - # H unsigned short n - # x pad byte x - # B uchar C - # H unsigned short n - # 4x pad byte x4 - HEADER = "C2nxCnx4" - HEADER_SIZE = Qpid::packed_size(HEADER) - MAX_PAYLOAD = 65535 - HEADER_SIZE - - def initialize(flags, type, track, channel, payload) - if payload.size > MAX_PAYLOAD - raise ArgumentError, "max payload size exceeded: #{payload.size}" - end - - @flags = flags - @type = type - @track = track - @channel = channel - @payload = payload - end - - def first_segment? ; FIRST_SEG & @flags > 0 ; end - - def last_segment? ; LAST_SEG & @flags > 0 ; end - - def first_frame? ; FIRST_FRM & @flags > 0 ; end - - def last_frame? ; LAST_FRM & @flags > 0 ; end - - def to_s - fs = first_segment? ? 'S' : '.' - ls = last_segment? ? 's' : '.' - ff = first_frame? ? 'F' : '.' - lf = last_frame? ? 'f' : '.' - - return "%s%s%s%s %s %s %s %s" % [fs, ls, ff, lf, - @type, - @track, - @channel, - @payload.inspect] - end - end - - class FramingError < Exception ; end - - class Closed < Exception ; end - - class Framer - include Packer - - # Python: "!4s4B" - HEADER = "a4C4" - HEADER_SIZE = 8 - - def raw - Qpid::raw_logger - end - - def frm - Qpid::frm_logger - end - - def initialize(sock) - @sock = sock - @sock.extend(MonitorMixin) - @tx_buf = "" - @rx_buf = "" - @security_layer_tx = nil - @security_layer_rx = nil - @maxbufsize = 65535 - end - - attr_reader :sock - attr_accessor :security_layer_tx, :security_layer_rx - - def aborted? ; false ; end - - def write(buf) - @tx_buf += buf - end - - def flush - @sock.synchronize do - if @security_layer_tx - cipher_buf = Sasl.encode(@security_layer_tx, @tx_buf) - _write(cipher_buf) - else - _write(@tx_buf) - end - @tx_buf = "" - frm.debug("FLUSHED") if frm - end - rescue - @sock.close unless @sock.closed? - end - - def _write(buf) - while buf && buf.size > 0 - # FIXME: Catch errors - n = @sock.write(buf) - raw.debug("SENT #{buf[0, n].inspect}") if raw - buf[0,n] = "" - @sock.flush - end - end - - def read(n) - while @rx_buf.size < n - begin - s = @sock.recv(@maxbufsize) - if @security_layer_rx - s = Sasl.decode(@security_layer_rx, s) - end - rescue IOError => e - raise e if @rx_buf != "" - @sock.close unless @sock.closed? - raise Closed - end - # FIXME: Catch errors - if s.nil? or s.size == 0 - @sock.close unless @sock.closed? - raise Closed - end - @rx_buf += s - raw.debug("RECV #{n}/#{@rx_buf.size} #{s.inspect}") if raw - end - data = @rx_buf[0, n] - @rx_buf = @rx_buf[n, @rx_buf.size - n] - return data - end - - def read_header - unpack(Framer::HEADER, Framer::HEADER_SIZE) - end - - def write_header(major, minor) - @sock.synchronize do - pack(Framer::HEADER, "AMQP", 1, 1, major, minor) - flush() - end - end - - def write_frame(frame) - @sock.synchronize do - size = frame.payload.size + Frame::HEADER_SIZE - track = frame.track & 0x0F - pack(Frame::HEADER, frame.flags, frame.type, size, track, frame.channel) - write(frame.payload) - if frame.last_segment? and frame.last_frame? - flush() - frm.debug("SENT #{frame}") if frm - end - end - end - - def read_frame - flags, type, size, track, channel = unpack(Frame::HEADER, Frame::HEADER_SIZE) - raise FramingError if (flags & 0xF0 > 0) - payload = read(size - Frame::HEADER_SIZE) - frame = Frame.new(flags, type, track, channel, payload) - frm.debug("RECV #{frame}") if frm - return frame - end - end -end diff --git a/ruby/lib/qpid/invoker.rb b/ruby/lib/qpid/invoker.rb deleted file mode 100644 index 39716ac6c2..0000000000 --- a/ruby/lib/qpid/invoker.rb +++ /dev/null @@ -1,65 +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 Qpid::Invoker - - # Requires that client defines a invoke method and overrides - # resolve_method - - # FIXME: Is it really worth defining methods in method_missing ? We - # could just dispatch there directly - - def invc_method(name, resolved) - define_singleton_method(name) { |*args| invoke(resolved, args) } - # FIXME: the Python code also attaches docs from resolved.pydoc - end - - def invc_value(name, resolved) - define_singleton_method(name) { | | resolved } - end - - def invc_error(name, resolved) - msg = "%s instance has no attribute '%s'" % [self.class.name, name] - if resolved - msg += "\n%s" % resolved - end - raise NameError, msg - end - - def resolve_method(name) - invocation(:error, nil) - end - - def method_missing(name, *args) - disp, resolved = resolve_method(name) - disp.call(name, resolved) - send(name, *args) - end - - def invocation(kind, name = nil) - [ method("invc_#{kind}"), name ] - end - - private - def define_singleton_method(name, &body) - singleton_class = class << self; self; end - singleton_class.send(:define_method, name, &body) - end - -end diff --git a/ruby/lib/qpid/packer.rb b/ruby/lib/qpid/packer.rb deleted file mode 100644 index ae1be37faf..0000000000 --- a/ruby/lib/qpid/packer.rb +++ /dev/null @@ -1,33 +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 - module Packer - def unpack(fmt, len) - raw = read(len) - values = raw.unpack(fmt) - values = values[0] if values.size == 1 - return values - end - - def pack(fmt, *args) - write(args.pack(fmt)) - end - end -end diff --git a/ruby/lib/qpid/peer.rb b/ruby/lib/qpid/peer.rb deleted file mode 100644 index cdb962169b..0000000000 --- a/ruby/lib/qpid/peer.rb +++ /dev/null @@ -1,289 +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 "thread" -require "qpid/queue" -require "qpid/connection08" -require "qpid/fields" - -module Qpid08 - - Queue = Qpid::Queue - - class Peer - - def initialize(conn, delegate) - @conn = conn - @delegate = delegate - @outgoing = Queue.new() - @work = Queue.new() - @channels = {} - @mutex = Mutex.new() - end - - def channel(id) - @mutex.synchronize do - ch = @channels[id] - if ch.nil? - ch = Channel.new(id, self, @outgoing, @conn.spec) - @channels[id] = ch - end - return ch - end - end - - def channel_delete(id) - @channels.delete(id) - end - - def start() - spawn(:writer) - spawn(:reader) - spawn(:worker) - end - - def close() - @mutex.synchronize do - @channels.each_value do |ch| - ch.close() - end - @outgoing.close() - @work.close() - end - end - - private - - def spawn(method, *args) - Thread.new do - begin - send(method, *args) - # is this the standard way to catch any exception? - rescue Closed => e - puts "#{method} #{e}" - rescue Object => e - print e - e.backtrace.each do |line| - print "\n ", line - end - print "\n" - end - end - end - - def reader() - while true - frame = @conn.read() - ch = channel(frame.channel) - ch.dispatch(frame, @work) - end - end - - def writer() - while true - @conn.write(@outgoing.get()) - end - end - - def worker() - while true - dispatch(@work.get()) - end - end - - def dispatch(queue) - frame = queue.get() - ch = channel(frame.channel) - payload = frame.payload - if payload.method.content? - content = Qpid08::read_content(queue) - else - content = nil - end - - message = Message.new(payload.method, payload.args, content) - @delegate.dispatch(ch, message) - end - - end - - class Channel - def initialize(id, peer, outgoing, spec) - @id = id - @peer = peer - @outgoing = outgoing - @spec = spec - @incoming = Queue.new() - @responses = Queue.new() - @queue = nil - @closed = false - end - - attr_reader :id - - def closed?; @closed end - - def close() - return if closed? - @peer.channel_delete(@id) - @closed = true - @incoming.close() - @responses.close() - end - - def dispatch(frame, work) - payload = frame.payload - case payload - when Method - if payload.method.response? - @queue = @responses - else - @queue = @incoming - work << @incoming - end - end - @queue << frame - end - - def method_missing(name, *args) - method = @spec.find_method(name) - if method.nil? - raise NoMethodError.new("undefined method '#{name}' for #{self}:#{self.class}") - end - - if args.size == 1 and args[0].instance_of? Hash - kwargs = args[0] - invoke_args = method.fields.map do |f| - kwargs[f.name] - end - content = kwargs[:content] - else - invoke_args = [] - method.fields.each do |f| - if args.any? - invoke_args << args.shift() - else - invoke_args << f.default - end - end - if method.content? and args.any? - content = args.shift() - else - content = nil - end - if args.any? then raise ArgumentError.new("#{args.size} extr arguments") end - end - return invoke(method, invoke_args, content) - end - - def invoke(method, args, content = nil) - raise Closed() if closed? - frame = Frame.new(@id, Method.new(method, args)) - @outgoing << frame - - if method.content? - content = Content.new() if content.nil? - write_content(method.parent, content, @outgoing) - end - - nowait = false - f = method.fields[:"nowait"] - nowait = args[method.fields.index(f)] unless f.nil? - - unless nowait or method.responses.empty? - resp = @responses.get().payload - if resp.method.content? - content = read_content(@responses) - else - content = nil - end - if method.responses.include? resp.method - return Message.new(resp.method, resp.args, content) - else - # XXX: ValueError doesn't actually exist - raise ValueError.new(resp) - end - end - end - - def write_content(klass, content, queue) - size = content.size - header = Frame.new(@id, Header.new(klass, content.weight, size, content.headers)) - queue << header - content.children.each {|child| write_content(klass, child, queue)} - queue << Frame.new(@id, Body.new(content.body)) if size > 0 - end - - end - - def Qpid08.read_content(queue) - frame = queue.get() - header = frame.payload - children = [] - 1.upto(header.weight) { children << read_content(queue) } - size = header.size - read = 0 - buf = "" - while read < size - body = queue.get() - content = body.payload.content - buf << content - read += content.size - end - buf.freeze() - return Content.new(header.properties.clone(), buf, children) - end - - class Content - def initialize(headers = {}, body = "", children = []) - @headers = headers - @body = body - @children = children - end - - attr_reader :headers, :body, :children - - def size; body.size end - def weight; children.size end - - def [](key); @headers[key] end - def []=(key, value); @headers[key] = value end - end - - class Message - fields(:method, :args, :content) - - alias fields args - - def method_missing(name) - return args[@method.fields[name].id] - end - - def inspect() - "#{method.qname}(#{args.join(", ")})" - end - end - - module Delegate - def dispatch(ch, msg) - send(msg.method.qname, ch, msg) - end - end - -end diff --git a/ruby/lib/qpid/qmf.rb b/ruby/lib/qpid/qmf.rb deleted file mode 100644 index 4711d355cd..0000000000 --- a/ruby/lib/qpid/qmf.rb +++ /dev/null @@ -1,1957 +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. -# - -# Console API for Qpid Management Framework - -require 'socket' -require 'monitor' -require 'thread' -require 'uri' -require 'time' - -module Qpid::Qmf - - # To access the asynchronous operations, a class must be derived from - # Console with overrides of any combination of the available methods. - class Console - - # Invoked when a connection is established to a broker - def broker_connected(broker); end - - # Invoked when the connection to a broker is lost - def broker_disconnected(broker); end - - # Invoked when a QMF package is discovered - def new_package(name); end - - # Invoked when a new class is discovered. Session.getSchema can be - # used to obtain details about the class - def new_class(kind, klass_key); end - - # Invoked when a QMF agent is discovered - def new_agent(agent); end - - # Invoked when a QMF agent disconects - def del_agent(agent); end - - # Invoked when an object is updated - def object_props(broker, record); end - - # Invoked when an object is updated - def object_stats(broker, record); end - - # Invoked when an event is raised - def event(broker, event); end - - # Invoked when an agent heartbeat is received. - def heartbeat(agent, timestamp); end - - # Invoked when the connection sequence reaches the point where broker information is available. - def broker_info(broker); end - - # Invoked when a method response from an asynchronous method call is received. - def method_response(broker, seq, response); end - end - - class BrokerURL - - attr_reader :host, :port, :auth_name, :auth_pass - - def initialize(text) - uri = URI.parse(text) - - @host = uri.host - @port = uri.port ? uri.port : 5672 - @auth_name = uri.user - @auth_pass = uri.password - - return uri - end - - def name - "#{@host}:#{@port}" - end - - def match(host, port) - # FIXME: Unlcear what the Python code is actually checking for - # here, especially since HOST can resolve to multiple IP's - @port == port && - (host == @host || ipaddr(host, port) == ipaddr(@host, @port)) - end - - private - def ipaddr(host, port) - s = Socket::getaddrinfo(host, port, - Socket::AF_INET, Socket::SOCK_STREAM) - s[0][2] - end - end - - # An instance of the Session class represents a console session running - # against one or more QMF brokers. A single instance of Session is - # needed to interact with the management framework as a console. - class Session - CONTEXT_SYNC = 1 - CONTEXT_STARTUP = 2 - CONTEXT_MULTIGET = 3 - - DEFAULT_GET_WAIT_TIME = 60 - - include MonitorMixin - - attr_reader :binding_key_list, :select, :seq_mgr, :console, :packages - - # Initialize a session. If the console argument is provided, the - # more advanced asynchronous features are available. If console is - # defaulted, the session will operate in a simpler, synchronous - # manner. The rcvObjects, rcvEvents, and rcvHeartbeats arguments - # are meaningful only if 'console' is provided. They control - # whether object updates, events, and agent-heartbeats are - # subscribed to. If the console is not interested in receiving one - # or more of the above, setting the argument to False will reduce - # tha bandwidth used by the API. If manageConnections is set to - # True, the Session object will manage connections to the brokers. - # This means that if a broker is unreachable, it will retry until a - # connection can be established. If a connection is lost, the - # Session will attempt to reconnect. - # - # If manageConnections is set to False, the user is responsible for - # handing failures. In this case, an unreachable broker will cause - # addBroker to raise an exception. If userBindings is set to False - # (the default) and rcvObjects is True, the console will receive - # data for all object classes. If userBindings is set to True, the - # user must select which classes the console shall receive by - # invoking the bindPackage or bindClass methods. This allows the - # console to be configured to receive only information that is - # relavant to a particular application. If rcvObjects id False, - # userBindings has no meaning. - # - # Accept a hash of parameters, where keys can be :console, - # :rcv_objects, :rcv_events, :rcv_heartbeats, :manage_connections, - # and :user_bindings - def initialize(kwargs = {}) - super() - @console = kwargs[:console] || nil - @brokers = [] - @packages = {} - @seq_mgr = SequenceManager.new - @cv = new_cond - @sync_sequence_list = [] - @result = [] - @select = [] - @error = nil - @rcv_objects = kwargs[:rcv_objects] == nil ? true : kwargs[:rcv_objects] - @rcv_events = kwargs[:rcv_events] == nil ? true : kwargs[:rcv_events] - @rcv_heartbeats = kwargs[:rcv_heartbeats] == nil ? true : kwargs[:rcv_heartbeats] - @user_bindings = kwargs[:user_bindings] == nil ? false : kwargs[:user_bindings] - unless @console - @rcv_objects = false - @rcv_events = false - @rcv_heartbeats = false - end - @binding_key_list = binding_keys - @manage_connections = kwargs[:manage_connections] || false - - if @user_bindings && ! @rcv_objects - raise ArgumentError, "user_bindings can't be set unless rcv_objects is set and a console is provided" - end - - end - - def to_s - "QMF Console Session Manager (brokers: #{@brokers.size})" - end - - def managedConnections? - return @manage_connections - end - - # Connect to a Qpid broker. Returns an object of type Broker - # - # To supply a username for authentication, use the URL syntax: - # - # amqp://username@hostname:port - # - # If the broker needs a password for the client, an interactive prompt will be - # provided to the user. - # - # To supply a username and a password, use - # - # amqp://username:password@hostname:port - # - # The following keyword arguments may be used to control authentication: - # - # :mechanism - SASL mechanism (i.e. "PLAIN", "GSSAPI", "ANONYMOUS", etc. - # - defaults to unspecified (the system chooses for you) - # :service - SASL service name (i.e. the kerberos principal of the broker) - # - defaults to "qpidd" - # :min_ssf - Minimum Security Strength Factor for SASL security layers - # - defaults to 0 - # :max_ssf - Maximum Security Strength Factor for SASL security layers - # - defaults to 65535 - # - def add_broker(target = "amqp://localhost", kwargs = {}) - url = BrokerURL.new(target) - broker = Broker.new(self, url.host, url.port, url.auth_name, url.auth_pass, kwargs) - unless broker.connected? || @manage_connections - raise broker.error - end - - @brokers << broker - objects(:broker => broker, :class => "agent") unless @manage_connections - return broker - end - - # Disconnect from a broker. The 'broker' argument is the object - # returned from the addBroker call - def del_broker(broker) - broker.shutdown - @brokers.delete(broker) - end - - # Get the list of known classes within a QMF package - def classes(package_name) - list = [] - @brokers.each { |broker| broker.wait_for_stable } - if @packages.include?(package_name) - # FIXME What's the actual structure of @packages[package_name] - @packages[package_name].each do |key, schema_class| - list << schema_class.klass_key - end - end - return list - end - - # Get the schema for a QMF class - def schema(klass_key) - @brokers.each { |broker| broker.wait_for_stable } - if @packages.include?(klass_key.package) - @packages[klass_key.package][ [klass_key.klass_name, klass_key.hash] ] - end - end - - def bind_package(package_name) - unless @user_bindings && @rcv_objects - raise "userBindings option not set for Session" - end - @brokers.each do |broker| - args = { :exchange => "qpid.management", - :queue => broker.topic_name, - :binding_key => "console.obj.*.*.#{package_name}.#" } - broker.amqp_session.exchange_bind(args) - end - end - - def bind_class(package_name, class_name) - unless @user_bindings && @rcv_objects - raise "userBindings option not set for Session" - end - @brokers.each do |broker| - args = { :exchange => "qpid.management", - :queue => broker.topic_name, - :binding_key=> "console.obj.*.*.#{package_name}.#{class_name}.#" } - broker.amqp_session.exchange_bind(args) - end - end - - def bind_class_key(klass_key) - unless @user_bindings && @rcv_objects - raise "userBindings option not set for Session" - end - pname, cname, hash = klass_key.to_a() - @brokers.each do |broker| - args = { :exchange => "qpid.management", - :queue => broker.topic_name, - :binding_key => "console.obj.*.*.#{pname}.#{cname}.#" } - broker.amqp_session.exchange_bind(args) - end - end - - # Get a list of currently known agents - def agents(broker=nil) - broker_list = [] - if broker.nil? - broker_list = @brokers.dup - else - broker_list << broker - end - broker_list.each { |b| b.wait_for_stable } - agent_list = [] - broker_list.each { |b| agent_list += b.agents } - return agent_list - end - - # Get a list of objects from QMF agents. - # All arguments are passed by name(keyword). - # - # The class for queried objects may be specified in one of the - # following ways: - # :schema => <schema> - supply a schema object returned from getSchema. - # :key => <key> - supply a klass_key from the list returned by getClasses. - # :class => <name> - supply a class name as a string. If the class name exists - # in multiple packages, a _package argument may also be supplied. - # :object_id = <id> - get the object referenced by the object-id - # - # If objects should be obtained from only one agent, use the following argument. - # Otherwise, the query will go to all agents. - # - # :agent = <agent> - supply an agent from the list returned by getAgents. - # - # If the get query is to be restricted to one broker (as opposed to - # all connected brokers), add the following argument: - # - # :broker = <broker> - supply a broker as returned by addBroker. - # - # The default timeout for this synchronous operation is 60 seconds. To change the timeout, - # use the following argument: - # - # :timeout = <time in seconds> - # - # If additional arguments are supplied, they are used as property - # selectors, as long as their keys are strings. For example, if - # the argument "name" => "test" is supplied, only objects whose - # "name" property is "test" will be returned in the result. - def objects(kwargs) - if kwargs.include?(:broker) - broker_list = [] - broker_list << kwargs[:broker] - else - broker_list = @brokers - end - broker_list.each { |broker| - broker.wait_for_stable - if kwargs[:package] != "org.apache.qpid.broker" or kwargs[:class] != "agent" - objects(:agent => broker.agent(1,0), :package => "org.apache.qpid.broker", :class => "agent") if broker.connected? - end - } - - agent_list = [] - if kwargs.include?(:agent) - agent = kwargs[:agent] - unless broker_list.include?(agent.broker) - raise ArgumentError, "Supplied agent is not accessible through the supplied broker" - end - agent_list << agent if agent.broker.connected? - else - if kwargs.include?(:object_id) - oid = kwargs[:object_id] - broker_list.each { |broker| - broker.agents.each { |agent| - if oid.broker_bank == agent.broker_bank && oid.agent_bank == agent.agent_bank - agent_list << agent if agent.broker.connected? - end - } - } - else - broker_list.each { |broker| - agent_list += broker.agents if broker.connected? - } - end - end - - cname = nil - if kwargs.include?(:schema) - # FIXME: What kind of object is kwargs[:schema] - pname, cname, hash = kwargs[:schema].getKey().to_a - elsif kwargs.include?(:key) - pname, cname, hash = kwargs[:key].to_a - elsif kwargs.include?(:class) - pname, cname, hash = [kwargs[:package], kwargs[:class], nil] - end - if cname.nil? && ! kwargs.include?(:object_id) - raise ArgumentError, - "No class supplied, use :schema, :key, :class, or :object_id' argument" - end - - map = {} - @select = [] - if kwargs.include?(:object_id) - map["_objectid"] = kwargs[:object_id].to_s - else - map["_class"] = cname - map["_package"] = pname if pname - map["_hash"] = hash if hash - kwargs.each do |k,v| - @select << [k, v] if k.is_a?(String) - end - end - - @result = [] - agent_list.each do |agent| - broker = agent.broker - send_codec = Qpid::StringCodec.new(broker.conn.spec) - seq = nil - synchronize do - seq = @seq_mgr.reserve(CONTEXT_MULTIGET) - @sync_sequence_list << seq - end - broker.set_header(send_codec, ?G, seq) - send_codec.write_map(map) - bank_key = "%d.%d" % [broker.broker_bank, agent.agent_bank] - smsg = broker.message(send_codec.encoded, "agent.#{bank_key}") - broker.emit(smsg) - end - - timeout = false - if kwargs.include?(:timeout) - wait_time = kwargs[:timeout] - else - wait_time = DEFAULT_GET_WAIT_TIME - end - synchronize do - unless @cv.wait_for(wait_time) { @sync_sequence_list.empty? || @error } - @sync_sequence_list.each do |pending_seq| - @seq_mgr.release(pending_seq) - end - @sync_sequence_list = [] - timeout = true - end - end - - if @error - errorText = @error - @error = nil - raise errorText - end - - if @result.empty? && timeout - raise "No agent responded within timeout period" - end - @result - end - - # Return one and only one object or nil. - def object(kwargs) - objs = objects(kwargs) - return objs.length == 1 ? objs[0] : nil - end - - # Return the first of potentially many objects. - def first_object(kwargs) - objs = objects(kwargs) - return objs.length > 0 ? objs[0] : nil - end - - def set_event_filter(kwargs); end - - def handle_broker_connect(broker); end - - def handle_broker_resp(broker, codec, seq) - broker.broker_id = codec.read_uuid - @console.broker_info(broker) if @console - - # Send a package request - # (effectively inc and dec outstanding by not doing anything) - send_codec = Qpid::StringCodec.new(broker.conn.spec) - seq = @seq_mgr.reserve(CONTEXT_STARTUP) - broker.set_header(send_codec, ?P, seq) - smsg = broker.message(send_codec.encoded) - broker.emit(smsg) - end - - def handle_package_ind(broker, codec, seq) - pname = codec.read_str8 - new_package = false - synchronize do - new_package = ! @packages.include?(pname) - @packages[pname] = {} if new_package - end - @console.new_package(pname) if @console - - # Send a class request - broker.inc_outstanding - send_codec = Qpid::StringCodec.new(broker.conn.spec) - seq = @seq_mgr.reserve(CONTEXT_STARTUP) - broker.set_header(send_codec, ?Q, seq) - send_codec.write_str8(pname) - smsg = broker.message(send_codec.encoded) - broker.emit(smsg) - end - - def handle_command_complete(broker, codec, seq) - code = codec.read_uint32 - text = codec.read_str8 - context = @seq_mgr.release(seq) - if context == CONTEXT_STARTUP - broker.dec_outstanding - elsif context == CONTEXT_SYNC && seq == broker.sync_sequence - broker.sync_done - elsif context == CONTEXT_MULTIGET && @sync_sequence_list.include?(seq) - synchronize do - @sync_sequence_list.delete(seq) - @cv.signal if @sync_sequence_list.empty? - end - end - end - - def handle_class_ind(broker, codec, seq) - kind = codec.read_uint8 - classKey = ClassKey.new(codec) - unknown = false - - synchronize do - return unless @packages.include?(classKey.package) - unknown = true unless @packages[classKey.package].include?([classKey.klass_name, classKey.hash]) - end - - - if unknown - # Send a schema request for the unknown class - broker.inc_outstanding - send_codec = Qpid::StringCodec.new(broker.conn.spec) - seq = @seq_mgr.reserve(CONTEXT_STARTUP) - broker.set_header(send_codec, ?S, seq) - classKey.encode(send_codec) - smsg = broker.message(send_codec.encoded) - broker.emit(smsg) - end - end - - def handle_method_resp(broker, codec, seq) - code = codec.read_uint32 - text = codec.read_str16 - out_args = {} - pair = @seq_mgr.release(seq) - return unless pair - method, synchronous = pair - if code == 0 - method.arguments.each do |arg| - if arg.dir.index(?O) - out_args[arg.name] = decode_value(codec, arg.type) - end - end - end - result = MethodResult.new(code, text, out_args) - if synchronous: - broker.synchronize do - broker.sync_result = MethodResult.new(code, text, out_args) - broker.sync_done - end - else - @console.method_response(broker, seq, result) if @console - end - end - - def handle_heartbeat_ind(broker, codec, seq, msg) - if @console - broker_bank = 1 - agent_bank = 0 - dp = msg.get("delivery_properties") - if dp - key = dp["routing_key"] - key_elements = key.split(".") - if key_elements.length == 4 - broker_bank = key_elements[2].to_i - agent_bank = key_elements[3].to_i - end - end - agent = broker.agent(broker_bank, agent_bank) - timestamp = codec.read_uint64 - @console.heartbeat(agent, timestamp) if agent - end - end - - def handle_event_ind(broker, codec, seq) - if @console - event = Event.new(self, broker, codec) - @console.event(broker, event) - end - end - - def handle_schema_resp(broker, codec, seq) - kind = codec.read_uint8 - classKey = ClassKey.new(codec) - klass = SchemaClass.new(self, kind, classKey, codec) - synchronize { @packages[classKey.package][ [classKey.klass_name, classKey.hash] ] = klass } - - @seq_mgr.release(seq) - broker.dec_outstanding - @console.new_class(kind, classKey) if @console - end - - def handle_content_ind(broker, codec, seq, prop=false, stat=false) - klass_key = ClassKey.new(codec) - pname, cname, hash = klass_key.to_a() ; - - schema = nil - synchronize do - return unless @packages.include?(klass_key.package) - return unless @packages[klass_key.package].include?([klass_key.klass_name, klass_key.hash]) - schema = @packages[klass_key.package][ [klass_key.klass_name, klass_key.hash] ] - end - - - object = Qpid::Qmf::Object.new(self, broker, schema, codec, prop, stat) - if pname == "org.apache.qpid.broker" && cname == "agent" && prop - broker.update_agent(object) - end - - synchronize do - if @sync_sequence_list.include?(seq) - if object.timestamps()[2] == 0 && select_match(object) - @result << object - end - return - end - end - - @console.object_props(broker, object) if @console && @rcv_objects && prop - @console.object_stats(broker, object) if @console && @rcv_objects && stat - end - - def handle_broker_disconnect(broker); end - - def handle_error(error) - synchronize do - @error = error if @sync_sequence_list.length > 0 - @sync_sequence_list = [] - @cv.signal - end - end - - # Decode, from the codec, a value based on its typecode - def decode_value(codec, typecode) - case typecode - when 1: data = codec.read_uint8 # U8 - when 2: data = codec.read_uint16 # U16 - when 3: data = codec.read_uint32 # U32 - when 4: data = codec.read_uint64 # U64 - when 6: data = codec.read_str8 # SSTR - when 7: data = codec.read_str16 # LSTR - when 8: data = codec.read_int64 # ABSTIME - when 9: data = codec.read_uint64 # DELTATIME - when 10: data = ObjectId.new(codec) # REF - when 11: data = codec.read_uint8 != 0 # BOOL - when 12: data = codec.read_float # FLOAT - when 13: data = codec.read_double # DOUBLE - when 14: data = codec.read_uuid # UUID - when 15: data = codec.read_map # FTABLE - when 16: data = codec.read_int8 # S8 - when 17: data = codec.read_int16 # S16 - when 18: data = codec.read_int32 # S32 - when 19: data = codec.read_int64 # S64 - when 20: # Object - inner_type_code = codec.read_uint8() - if (inner_type_code == 20) - classKey = ClassKey.new(codec) - innerSchema = schema(classKey) - data = Object.new(self, @broker, innerSchema, codec, true, true, false) if innerSchema - else - data = decode_value(codec, inner_type_code) - end - when 21: - data = [] - rec_codec = Qpid::StringCodec.new(codec.spec, codec.read_vbin32()) - count = rec_codec.read_uint32() - while count > 0 do - type = rec_codec.read_uint8() - data << (decode_value(rec_codec,type)) - count -= 1 - end - when 22: - data = [] - rec_codec = Qpid::StringCodec.new(codec.spec, codec.read_vbin32()) - count = rec_codec.read_uint32() - type = rec_codec.read_uint8() - while count > 0 do - data << (decode_value(rec_codec,type)) - count -= 1 - end - else - raise ArgumentError, "Invalid type code: #{typecode} - #{typecode.inspect}" - end - return data - end - - ENCODINGS = { - String => 6, - Fixnum => 18, - Bignum => 19, - Float => 12, - Array => 21, - Hash => 15 - } - - def encoding(object) - klass = object.class - if ENCODINGS.has_key?(klass) - return ENCODINGS[klass] - end - for base in klass.__bases__ - result = encoding(base) - return result unless result.nil? - end - end - - # Encode, into the codec, a value based on its typecode - def encode_value(codec, value, typecode) - # FIXME: Python does a lot of magic type conversions - # We just assume that value has the right type; this is safer - # than coercing explicitly, since Array::pack will complain - # loudly about various type errors - case typecode - when 1: codec.write_uint8(value) # U8 - when 2: codec.write_uint16(value) # U16 - when 3: codec.write_uint32(value) # U32 - when 4: codec.write_uint64(value) # U64 - when 6: codec.write_str8(value) # SSTR - when 7: codec.write_str16(value) # LSTR - when 8: codec.write_int64(value) # ABSTIME - when 9: codec.write_uint64(value) # DELTATIME - when 10: value.encode(codec) # REF - when 11: codec.write_uint8(value ? 1 : 0) # BOOL - when 12: codec.write_float(value) # FLOAT - when 13: codec.write_double(value) # DOUBLE - when 14: codec.write_uuid(value) # UUID - when 15: codec.write_map(value) # FTABLE - when 16: codec.write_int8(value) # S8 - when 17: codec.write_int16(value) # S16 - when 18: codec.write_int32(value) # S32 - when 19: codec.write_int64(value) # S64 - when 20: value.encode(codec) - when 21: # List - send_codec = Qpid::StringCodec.new(codec.spec) - encode_value(send_codec, value.size, 3) - value.each do v - ltype = encoding(v) - encode_value(send_codec,ltype,1) - encode_value(send_codec,v,ltype) - end - codec.write_vbin32(send_codec.encoded) - when 22: # Array - send_codec = Qpid::StringCodec.new(codec.spec) - encode_value(send_codec, value.size, 3) - if value.size > 0 - ltype = encoding(value[0]) - encode_value(send_codec,ltype,1) - value.each do v - encode_value(send_codec,v,ltype) - end - end - codec.write_vbin32(send_codec.encoded) - else - raise ValueError, "Invalid type code: %d" % typecode - end - end - - def display_value(value, typecode) - case typecode - when 1: return value.to_s - when 2: return value.to_s - when 3: return value.to_s - when 4: return value.to_s - when 6: return value.to_s - when 7: return value.to_s - when 8: return strftime("%c", gmtime(value / 1000000000)) - when 9: return value.to_s - when 10: return value.to_s - when 11: return value ? 'T' : 'F' - when 12: return value.to_s - when 13: return value.to_s - when 14: return Qpid::UUID::format(value) - when 15: return value.to_s - when 16: return value.to_s - when 17: return value.to_s - when 18: return value.to_s - when 19: return value.to_s - when 20: return value.to_s - when 21: return value.to_s - when 22: return value.to_s - else - raise ValueError, "Invalid type code: %d" % typecode - end - end - - private - - def binding_keys - key_list = [] - key_list << "schema.#" - if @rcv_objects && @rcv_events && @rcv_heartbeats && - ! @user_bindings - key_list << "console.#" - else - if @rcv_objects && ! @user_bindings - key_list << "console.obj.#" - else - key_list << "console.obj.*.*.org.apache.qpid.broker.agent" - end - key_list << "console.event.#" if @rcv_events - key_list << "console.heartbeat.#" if @rcv_heartbeats - end - return key_list - end - - # Check the object against select to check for a match - def select_match(object) - select.each do |key, value| - object.properties.each do |prop, propval| - return false if key == prop.name && value != propval - end - end - return true - end - - end - - class Package - attr_reader :name - - def initialize(name) - @name = name - end - end - - # A ClassKey uniquely identifies a class from the schema. - class ClassKey - attr_reader :package, :klass_name, :hash - - def initialize(package="", klass_name="", hash=0) - if (package.kind_of?(Qpid::Codec)) - @package = package.read_str8() - @klass_name = package.read_str8() - @hash = package.read_bin128() - else - @package = package - @klass_name = klass_name - @hash = hash - end - end - - def encode(codec) - codec.write_str8(@package) - codec.write_str8(@klass_name) - codec.write_bin128(@hash) - end - - def to_a() - return [@package, @klass_name, @hash] - end - - def hash_string() - "%08x-%08x-%08x-%08x" % hash.unpack("NNNN") - end - - def to_s() - return "#{@package}:#{@klass_name}(#{hash_string()})" - end - end - - class SchemaClass - - CLASS_KIND_TABLE = 1 - CLASS_KIND_EVENT = 2 - - attr_reader :klass_key, :arguments, :super_klass_key - - def initialize(session, kind, key, codec) - @session = session - @kind = kind - @klass_key = key - @super_klass_key = nil - @properties = [] - @statistics = [] - @methods = [] - @arguments = [] - - has_supertype = 0 #codec.read_uint8 - if @kind == CLASS_KIND_TABLE - prop_count = codec.read_uint16 - stat_count = codec.read_uint16 - method_count = codec.read_uint16 - if has_supertype == 1 - @super_klass_key = ClassKey.new(codec) - end - prop_count.times { |idx| - @properties << SchemaProperty.new(codec) } - stat_count.times { |idx| - @statistics << SchemaStatistic.new(codec) } - method_count.times { |idx| - @methods<< SchemaMethod.new(codec) } - elsif @kind == CLASS_KIND_EVENT - arg_count = codec.read_uint16 - arg_count.times { |idx| - sa = SchemaArgument.new(codec, false) - @arguments << sa - } - end - end - - def is_table? - @kind == CLASS_KIND_TABLE - end - - def is_event? - @kind == CLASS_KIND_EVENT - end - - def properties(include_inherited = true) - returnValue = @properties - if !@super_klass_key.nil? && include_inherited - returnValue = @properties + @session.schema(@super_klass_key).properties - end - return returnValue - end - - def statistics(include_inherited = true) - returnValue = @statistics - if !@super_klass_key.nil? && include_inherited - returnValue = @statistics + @session.schema(@super_klass_key).statistics - end - return returnValue - end - - def methods(include_inherited = true) - returnValue = @methods - if !@super_klass_key.nil? && include_inherited - returnValue = @methods + @session.schema(@super_klass_key).methods - end - return returnValue - end - - def to_s - if @kind == CLASS_KIND_TABLE - kind_str = "Table" - elsif @kind == CLASS_KIND_EVENT - kind_str = "Event" - else - kind_str = "Unsupported" - end - "#{kind_str} Class: #{klass_key.to_s}" - end - end - - class SchemaProperty - - attr_reader :name, :type, :access, :index, :optional, - :unit, :min, :max, :maxlen, :desc, :refClass, :refPackage - - def initialize(codec) - map = codec.read_map - @name = map["name"] - @type = map["type"] - @access = map["access"] - @index = map["index"] != 0 - @optional = map["optional"] != 0 - @unit = map["unit"] - @min = map["min"] - @max = map["max"] - @maxlen = map["maxlen"] - @desc = map["desc"] - @refClass = map["refClass"] - @refPackage = map["refPackage"] - end - - def to_s - @name - end - end - - class SchemaStatistic - - attr_reader :name, :type, :unit, :desc, :refClass, :refPackage - - def initialize(codec) - map = codec.read_map - @name = map["name"] - @type = map["type"] - @unit = map["unit"] - @desc = map["desc"] - @refClass = map["refClass"] - @refPackage = map["refPackage"] - end - - def to_s - @name - end - end - - class SchemaMethod - - attr_reader :name, :desc, :arguments - - def initialize(codec) - map = codec.read_map - @name = map["name"] - arg_count = map["argCount"] - @desc = map["desc"] - @arguments = [] - arg_count.times { |idx| - @arguments << SchemaArgument.new(codec, true) - } - end - - def to_s - result = @name + "(" - first = true - result += @arguments.select { |arg| arg.dir.index(?I) }.join(", ") - result += ")" - return result - end - end - - class SchemaArgument - - attr_reader :name, :type, :dir, :unit, :min, :max, :maxlen - attr_reader :desc, :default, :refClass, :refPackage - - def initialize(codec, method_arg) - map = codec.read_map - @name = map["name"] - @type = map["type"] - @dir = map["dir"].upcase if method_arg - @unit = map["unit"] - @min = map["min"] - @max = map["max"] - @maxlen = map["maxlen"] - @desc = map["desc"] - @default = map["default"] - @refClass = map["refClass"] - @refPackage = map["refPackage"] - end - end - - # Object that represents QMF object identifiers - class ObjectId - - include Comparable - - attr_reader :first, :second - - def initialize(codec, first=0, second=0) - if codec - @first = codec.read_uint64 - @second = codec.read_uint64 - else - @first = first - @second = second - end - end - - def <=>(other) - return 1 unless other.is_a?(ObjectId) - return -1 if first < other.first - return 1 if first > other.first - return second <=> other.second - end - - def to_s - "%d-%d-%d-%d-%d" % [flags, sequence, broker_bank, agent_bank, object] - end - - def index - [first, second] - end - - def flags - (first & 0xF000000000000000) >> 60 - end - - def sequence - (first & 0x0FFF000000000000) >> 48 - end - - def broker_bank - (first & 0x0000FFFFF0000000) >> 28 - end - - def agent_bank - first & 0x000000000FFFFFFF - end - - def object - second - end - - def durable? - sequence == 0 - end - - def encode(codec) - codec.write_uint64(first) - codec.write_uint64(second) - end - end - - class Object - - DEFAULT_METHOD_WAIT_TIME = 60 - - attr_reader :object_id, :schema, :properties, :statistics, - :current_time, :create_time, :delete_time, :broker - - def initialize(session, broker, schema, codec, prop, stat, managed=true) - @session = session - @broker = broker - @schema = schema - if managed - @current_time = codec.read_uint64 - @create_time = codec.read_uint64 - @delete_time = codec.read_uint64 - @object_id = ObjectId.new(codec) - end - @properties = [] - @statistics = [] - if prop - missing = parse_presence_masks(codec, schema) - schema.properties.each do |property| - v = nil - unless missing.include?(property.name) - v = @session.decode_value(codec, property.type) - end - @properties << [property, v] - end - end - - if stat - schema.statistics.each do |statistic| - s = @session.decode_value(codec, statistic.type) - @statistics << [statistic, s] - end - end - end - - def klass_key - @schema.klass_key - end - - - def methods - @schema.methods - end - - # Return the current, creation, and deletion times for this object - def timestamps - return [@current_time, @create_time, @delete_time] - end - - # Return a string describing this object's primary key - def index - @properties.select { |property, value| - property.index - }.collect { |property,value| - @session.display_value(value, property.type) }.join(":") - end - - # Replace properties and/or statistics with a newly received update - def merge_update(newer) - unless object_id == newer.object_id - raise "Objects with different object-ids" - end - @properties = newer.properties unless newer.properties.empty? - @statistics = newer.statistics unless newer.statistics.empty? - end - - def update - obj = @session.object(:object_id => @object_id, :broker => @broker) - if obj - merge_update(obj) - else - raise "Underlying object no longer exists." - end - end - - def to_s - @schema.klass_key.to_s - end - - # This must be defined because ruby has this (deprecated) method built in. - def id - method_missing(:id) - end - - # Same here.. - def type - method_missing(:type) - end - - def name - method_missing(:name) - end - - def method_missing(name, *args) - name = name.to_s - - if method = @schema.methods.find { |method| name == method.name } - return invoke(method, name, args) - end - - @properties.each do |property, value| - return value if name == property.name - if name == "_#{property.name}_" && property.type == 10 - # Dereference references - deref = @session.objects(:object_id => value, :broker => @broker) - return nil unless deref.size == 1 - return deref[0] - end - end - @statistics.each do |statistic, value| - if name == statistic.name - return value - end - end - raise "Type Object has no attribute '#{name}'" - end - - def encode(codec) - codec.write_uint8(20) - @schema.klass_key.encode(codec) - - # emit presence masks for optional properties - mask = 0 - bit = 0 - schema.properties.each do |property| - if prop.optional - bit = 1 if bit == 0 - mask |= bit if value - bit = bit << 1 - if bit == 256 - bit = 0 - codec.write_uint8(mask) - mask = 0 - end - codec.write_uint8(mask) if bit != 0 - end - end - - # encode properties - @properties.each do |property, value| - @session.encode_value(codec, value, prop.type) if value - end - - # encode statistics - @statistics.each do |statistic, value| - @session.encode_value(codec, value, stat.type) - end - end - - private - - def send_method_request(method, name, args, synchronous = false, time_wait = nil) - @schema.methods.each do |schema_method| - if name == schema_method.name - send_codec = Qpid::StringCodec.new(@broker.conn.spec) - seq = @session.seq_mgr.reserve([schema_method, synchronous]) - @broker.set_header(send_codec, ?M, seq) - @object_id.encode(send_codec) - @schema.klass_key.encode(send_codec) - send_codec.write_str8(name) - - formals = method.arguments.select { |arg| arg.dir.index(?I) } - count = method.arguments.select { |arg| arg.dir.index(?I) }.size - unless formals.size == args.size - raise "Incorrect number of arguments: expected #{formals.size}, got #{args.size}" - end - - formals.zip(args).each do |formal, actual| - @session.encode_value(send_codec, actual, formal.type) - end - - ttl = time_wait ? time_wait * 1000 : nil - smsg = @broker.message(send_codec.encoded, - "agent.#{object_id.broker_bank}.#{object_id.agent_bank}", ttl=ttl) - @broker.sync_start if synchronous - @broker.emit(smsg) - - return seq - end - end - end - - def invoke(method, name, args) - kwargs = args[args.size - 1] - sync = true - timeout = DEFAULT_METHOD_WAIT_TIME - - if kwargs.class == Hash - if kwargs.include?(:timeout) - timeout = kwargs[:timeout] - end - - if kwargs.include?(:async) - sync = !kwargs[:async] - end - args.pop - end - - seq = send_method_request(method, name, args, synchronous = sync) - if seq - return seq unless sync - unless @broker.wait_for_sync_done(timeout) - @session.seq_mgr.release(seq) - raise "Timed out waiting for method to respond" - end - - if @broker.error - error_text = @broker.error - @broker.error = nil - raise error_text - end - - return @broker.sync_result - end - raise "Invalid Method (software defect) [#{name}]" - end - - def parse_presence_masks(codec, schema) - exclude_list = [] - bit = 0 - schema.properties.each do |property| - if property.optional - if bit == 0 - mask = codec.read_uint8 - bit = 1 - end - if (mask & bit) == 0 - exclude_list << property.name - end - bit *= 2 - bit = 0 if bit == 256 - end - end - return exclude_list - end - end - - class MethodResult - - attr_reader :status, :text, :out_args - - def initialize(status, text, out_args) - @status = status - @text = text - @out_args = out_args - end - - def method_missing(name) - name = name.to_s() - if @out_args.include?(name) - return @out_args[name] - else - raise "Unknown method result arg #{name}" - end - end - - def to_s - argsString = "" - padding = "" - out_args.each do |key,value| - argsString += padding - padding = " " if padding == "" - argsString += key.to_s - argsString += " => " - argsString += value.to_s() - end - "MethodResult(Msg: '#{text}' Status: #{status} Return: [#{argsString}])" - end - end - - class ManagedConnection - - DELAY_MIN = 1 - DELAY_MAX = 128 - DELAY_FACTOR = 2 - include MonitorMixin - - def initialize(broker) - super() - @broker = broker - @cv = new_cond - @is_cancelled = false - end - - # Main body of the running thread. - def start - @thread = Thread.new { - delay = DELAY_MIN - while true - begin - @broker.try_to_connect - synchronize do - while !@is_cancelled and @broker.connected? - @cv.wait - Thread.exit if @is_cancelled - delay = DELAY_MIN - end - end - - rescue - delay *= DELAY_FACTOR if delay < DELAY_MAX - end - - synchronize do - @cv.wait(delay) - Thread.exit if @is_cancelled - end - end - } - end - - # Tell this thread to stop running and return. - def stop - synchronize do - @is_cancelled = true - @cv.signal - end - end - - # Notify the thread that the connection was lost. - def disconnected - synchronize do - @cv.signal - end - end - - def join - @thread.join - end - end - - class Broker - - SYNC_TIME = 60 - @@next_seq = 1 - - include MonitorMixin - - attr_accessor :error - - attr_reader :amqp_session_id, :amqp_session, :conn, :broker_bank, :topic_name - - attr_accessor :broker_id, :sync_result - - def initialize(session, host, port, auth_name, auth_pass, kwargs) - super() - - # For debugging.. - Thread.abort_on_exception = true - - @session = session - @host = host - @port = port - @auth_name = auth_name - @auth_pass = auth_pass - @user_id = nil - @auth_mechanism = kwargs[:mechanism] - @auth_service = kwargs[:service] - @broker_bank = 1 - @topic_bound = false - @cv = new_cond - @error = nil - @broker_id = nil - @is_connected = false - @amqp_session_id = "%s.%d.%d" % [Socket.gethostname, Process::pid, @@next_seq] - @@next_seq += 1 - @conn = nil - if @session.managedConnections? - @thread = ManagedConnection.new(self) - @thread.start - else - @thread = nil - try_to_connect - end - end - - def connected? - @is_connected - end - - def agent(broker_bank, agent_bank) - bank_key = "%d.%d" % [broker_bank, agent_bank] - return @agents[bank_key] - end - - # Get the list of agents reachable via this broker - def agents - @agents.values - end - - def url - "#{@host}:#{@port}" - end - - def to_s - if connected? - "Broker connected at: #{url}" - else - "Disconnected Broker" - end - end - - def wait_for_sync_done(timeout=nil) - wait_time = timeout ? timeout : SYNC_TIME - synchronize do - return @cv.wait_for(wait_time) { ! @sync_in_flight || @error } - end - end - - def wait_for_stable - synchronize do - return unless connected? - return if @reqs_outstanding == 0 - @sync_in_flight = true - unless @cv.wait_for(SYNC_TIME) { @reqs_outstanding == 0 } - raise "Timed out waiting for broker to synchronize" - end - end - end - - # Compose the header of a management message - def set_header(codec, opcode, seq=0) - codec.write_uint8(?A) - codec.write_uint8(?M) - codec.write_uint8(?2) - codec.write_uint8(opcode) - codec.write_uint32(seq) - end - - def message(body, routing_key="broker", ttl=nil) - dp = @amqp_session.delivery_properties - dp.routing_key = routing_key - dp.ttl = ttl if ttl - mp = @amqp_session.message_properties - mp.content_type = "x-application/qmf" - mp.reply_to = amqp_session.reply_to("amq.direct", @reply_name) - #mp.user_id = @user_id if @user_id - return Qpid::Message.new(dp, mp, body) - end - - def emit(msg, dest="qpid.management") - @amqp_session.message_transfer(:destination => dest, - :message => msg) - end - - def inc_outstanding - synchronize { @reqs_outstanding += 1 } - end - - def dec_outstanding - synchronize do - @reqs_outstanding -= 1 - if @reqs_outstanding == 0 && ! @topic_bound - @topic_bound = true - @session.binding_key_list.each do |key| - args = { - :exchange => "qpid.management", - :queue => @topic_name, - :binding_key => key } - @amqp_session.exchange_bind(args) - end - end - if @reqs_outstanding == 0 && @sync_in_flight - sync_done - end - end - end - - def sync_start - synchronize { @sync_in_flight = true } - end - - def sync_done - synchronize do - @sync_in_flight = false - @cv.signal - end - end - - def update_agent(obj) - bank_key = "%d.%d" % [obj.brokerBank, obj.agentBank] - if obj.delete_time == 0 - unless @agents.include?(bank_key) - agent = Agent.new(self, obj.agentBank, obj.label) - @agents[bank_key] = agent - @session.console.new_agent(agent) if @session.console - end - else - agent = @agents.delete(bank_key) - @session.console.del_agent(agent) if agent && @session.console - end - end - - def shutdown - if @thread - @thread.stop - @thread.join - end - if connected? - @amqp_session.incoming("rdest").stop - if @session.console - @amqp_session.incoming("tdest").stop - end - @amqp_session.close - @is_connected = false - end - end - - def try_to_connect - @agents = {} - @agents["1.0"] = Agent.new(self, 0, "BrokerAgent") - @topic_bound = false - @sync_in_flight = false - @sync_request = 0 - @sync_result = nil - @reqs_outstanding = 1 - - # FIXME: Need sth for Qpid::Util::connect - - @conn = Qpid::Connection.new(TCPSocket.new(@host, @port), - :mechanism => @auth_mechanism, - :username => @auth_name, - :password => @auth_pass, - :host => @host, - :service => @auth_service) - @conn.start - @user_id = @conn.user_id - @reply_name = "reply-%s" % amqp_session_id - @amqp_session = @conn.session(@amqp_session_id) - @amqp_session.auto_sync = true - - @amqp_session.queue_declare(:queue => @reply_name, - :exclusive => true, - :auto_delete => true) - - @amqp_session.exchange_bind(:exchange => "amq.direct", - :queue => @reply_name, - :binding_key => @reply_name) - @amqp_session.message_subscribe(:queue => @reply_name, - :destination => "rdest", - :accept_mode => @amqp_session.message_accept_mode.none, - :acquire_mode => @amqp_session.message_acquire_mode.pre_acquired) - q = @amqp_session.incoming("rdest") - q.exc_listen(& method(:exception_cb)) - q.listen(& method(:reply_cb)) - @amqp_session.message_set_flow_mode(:destination => "rdest", - :flow_mode => 1) - @amqp_session.message_flow(:destination => "rdest", - :unit => 0, - :value => 0xFFFFFFFF) - @amqp_session.message_flow(:destination => "rdest", - :unit => 1, - :value => 0xFFFFFFFF) - - @topic_name = "topic-#{@amqp_session_id}" - @amqp_session.queue_declare(:queue => @topic_name, - :exclusive => true, - :auto_delete => true) - @amqp_session.message_subscribe(:queue => @topic_name, - :destination => "tdest", - :accept_mode => @amqp_session.message_accept_mode.none, - :acquire_mode => @amqp_session.message_acquire_mode.pre_acquired) - @amqp_session.incoming("tdest").listen(& method(:reply_cb)) - @amqp_session.message_set_flow_mode(:destination => "tdest", - :flow_mode => 1) - @amqp_session.message_flow(:destination => "tdest", - :unit => 0, - :value => 0xFFFFFFFF) - @amqp_session.message_flow(:destination => "tdest", - :unit => 1, - :value => 0xFFFFFFFF) - - @is_connected = true - @session.handle_broker_connect(self) - - codec = Qpid::StringCodec.new(@conn.spec) - set_header(codec, ?B) - msg = message(codec.encoded) - emit(msg) - end - - private - - # Check the header of a management message and extract the opcode and - # class - def check_header(codec) - begin - return [nil, nil] unless codec.read_uint8 == ?A - return [nil, nil] unless codec.read_uint8 == ?M - return [nil, nil] unless codec.read_uint8 == ?2 - opcode = codec.read_uint8 - seq = codec.read_uint32 - return [opcode, seq] - rescue - return [nil, nil] - end - end - - def reply_cb(msg) - codec = Qpid::StringCodec.new(@conn.spec, msg.body) - loop do - opcode, seq = check_header(codec) - return unless opcode - case opcode - when ?b: @session.handle_broker_resp(self, codec, seq) - when ?p: @session.handle_package_ind(self, codec, seq) - when ?z: @session.handle_command_complete(self, codec, seq) - when ?q: @session.handle_class_ind(self, codec, seq) - when ?m: @session.handle_method_resp(self, codec, seq) - when ?h: @session.handle_heartbeat_ind(self, codec, seq, msg) - when ?e: @session.handle_event_ind(self, codec, seq) - when ?s: @session.handle_schema_resp(self, codec, seq) - when ?c: @session.handle_content_ind(self, codec, seq, true, false) - when ?i: @session.handle_content_ind(self, codec, seq, false, true) - when ?g: @session.handle_content_ind(self, codec, seq, true, true) - else - raise "Unexpected opcode #{opcode.inspect}" - end - end - end - - def exception_cb(data) - @is_connected = false - @error = data - synchronize { @cv.signal if @sync_in_flight } - @session.handle_error(@error) - @session.handle_broker_disconnect(self) - @thread.disconnected if @thread - end - end - - class Agent - attr_reader :broker, :agent_bank, :label - - def initialize(broker, agent_bank, label) - @broker = broker - @agent_bank = agent_bank - @label = label - end - - def broker_bank - @broker.broker_bank - end - - def to_s - "Agent at bank %d.%d (%s)" % [@broker.broker_bank, @agent_bank, @label] - end - end - - class Event - - attr_reader :klass_key, :arguments, :timestamp, :name, :schema - - def initialize(session, broker, codec) - @session = session - @broker = broker - @klass_key = ClassKey.new(codec) - @timestamp = codec.read_int64 - @severity = codec.read_uint8 - @schema = nil - - pname, cname, hash = @klass_key.to_a() - session.packages.keys.each do |pname| - k = [cname, hash] - if session.packages[pname].include?(k) - @schema = session.packages[pname][k] - @arguments = {} - @schema.arguments.each do |arg| - v = session.decode_value(codec, arg.type) - @arguments[arg.name] = v - end - end - end - end - - def to_s - return "<uninterpretable>" unless @schema - t = Time.at(self.timestamp / 1000000000) - out = t.strftime("%c") - out += " " + sev_name + " " + @klass_key.package + ":" + @klass_key.klass_name - out += " broker=" + @broker.url - @schema.arguments.each do |arg| - out += " " + arg.name + "=" + @session.display_value(@arguments[arg.name], arg.type) - end - return out - end - - def sev_name - case @severity - when 0 : return "EMER " - when 1 : return "ALERT" - when 2 : return "CRIT " - when 3 : return "ERROR" - when 4 : return "WARN " - when 5 : return "NOTIC" - when 6 : return "INFO " - when 7 : return "DEBUG" - else - return "INV-%d" % @severity - end - end - - end - - # Manage sequence numbers for asynchronous method calls - class SequenceManager - include MonitorMixin - - def initialize - super() - @sequence = 0 - @pending = {} - end - - # Reserve a unique sequence number - def reserve (data) - synchronize do - result = @sequence - @sequence += 1 - @pending[result] = data - return result - end - end - - # Release a reserved sequence number - def release (seq) - synchronize { @pending.delete(seq) } - end - end - - class DebugConsole < Console - - def broker_connected(broker) - puts "brokerConnected #{broker}" - end - - def broker_disconnected(broker) - puts "brokerDisconnected #{broker}" - end - - def new_package(name) - puts "newPackage #{name}" - end - - def new_class(kind, klass_key) - puts "newClass #{kind} #{klass_key}" - end - - def new_agent(agent) - puts "new_agent #{agent}" - end - - def del_agent(agent) - puts "delAgent #{agent}" - end - - def object_props(broker, record) - puts "objectProps #{record}" - end - - def object_stats(broker, record) - puts "objectStats #{record}" - end - - def event(broker, event) - puts "event #{event}" - end - - def heartbeat(agent, timestamp) - puts "heartbeat #{agent}" - end - - def broker_info(broker) - puts "brokerInfo #{broker}" - end - end - - module XML - TYPES = { - 1 => "uint8", - 2 => "uint16", - 3 => "uint32", - 4 => "uint64", - 5 => "bool", - 6 => "short-stirng", - 7 => "long-string", - 8 => "abs-time", - 9 => "delta-time", - 10 => "reference", - 11 => "boolean", - 12 => "float", - 13 => "double", - 14 => "uuid", - 15 => "field-table", - 16 => "int8", - 17 => "int16", - 18 => "int32", - 19 => "int64", - 20 => "object", - 21 => "list", - 22 => "array" - } - - ACCESS_MODES = { - 1 => "RC", - 2 => "RW", - 3 => "RO" - } - - def common_attributes(item) - attr_string = "" - attr_string << " desc='#{item.desc}'" if item.desc - attr_string << " desc='#{item.desc}'" if item.desc - attr_string << " refPackage='#{item.refPackage}'" if item.refPackage - attr_string << " refClass='#{item.refClass}'" if item.refClass - attr_string << " unit='#{item.unit}'" if item.unit - attr_string << " min='#{item.min}'" if item.min - attr_string << " max='#{item.max}'" if item.max - attr_string << " maxlen='#{item.maxlen}'" if item.maxlen - return attr_string - end - - module_function :common_attributes - - def schema_xml(session, *packages) - schema = "<schemas>\n" - packages.each do |package| - schema << "\t<schema package='#{package}'>\n" - session.classes(package).each do |klass_key| - klass = session.schema(klass_key) - if klass.is_table? - if klass.super_klass_key - schema << "\t\t<class name='#{klass.klass_key.klass_name}' hash='#{klass.klass_key.hash_string}' extends='#{klass.super_klass_key.to_s}'>\n" - else - schema << "\t\t<class name='#{klass.klass_key.klass_name}' hash='#{klass.klass_key.hash_string}'>\n" - end - klass.properties(false).each do |property| - schema << "\t\t\t<property name='#{property.name}' type='#{TYPES[property.type]}' access='#{ACCESS_MODES[property.access]}' optional='#{property.optional ? "True" : "False"}'#{common_attributes(property)}/>\n" - end - klass.methods(false).each do |method| - schema << "\t\t\t<method name='#{method.name}'>\n" - method.arguments.each do |arg| - schema << "\t\t\t\t<arg name='#{arg.name}' dir='#{arg.dir}' type='#{TYPES[arg.type]}'#{common_attributes(arg)}/>\n" - end - schema << "\t\t\t</method>\n" - end - schema << "\t\t</class>\n" - else - schema << "\t\t<event name='#{klass.klass_key.klass_name}' hash='#{klass.klass_key.hash_string}'>\n" - klass.arguments.each do |arg| - schema << "\t\t\t<arg name='#{arg.name}'type='#{TYPES[arg.type]}'#{common_attributes(arg)}/>\n" - end - schema << "\t\t</event>\n" - end - end - schema << "\t</package>\n" - end - schema << "</schema>" - end - - module_function :schema_xml - end - -end diff --git a/ruby/lib/qpid/queue.rb b/ruby/lib/qpid/queue.rb deleted file mode 100644 index 4150173b53..0000000000 --- a/ruby/lib/qpid/queue.rb +++ /dev/null @@ -1,101 +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. -# - -# Augment the standard python multithreaded Queue implementation to add a -# close() method so that threads blocking on the content of a queue can be -# notified if the queue is no longer in use. - -require 'thread' - -# Python nominally uses a bounded queue, but the code never establishes -# a maximum size; we therefore use Ruby's unbounded queue -class Qpid::Queue < ::Queue - - DONE = Object.new - STOP = Object.new - - def initialize - super - @error = nil - @listener = nil - @exc_listener = nil - @exc_listener_lock = Monitor.new - @thread = nil - end - - def close(error = nil) - @error = error - put(DONE) - unless @thread.nil? - @thread.join() - @thread = nil - end - end - - def get(block = true, timeout = nil) - unless timeout.nil? - raise NotImplementedError - end - result = pop(! block) - if result == DONE - # this guarantees that any other waiting threads or any future - # calls to get will also result in a Qpid::Closed exception - put(DONE) - raise Qpid::Closed.new(@error) - else - return result - end - end - - alias :put :push - - def exc_listen(&block) - @exc_listener_lock.synchronize do - @exc_listener = block - end - end - - def listen(&block) - if ! block_given? && @thread - put(STOP) - @thread.join() - @thread = nil - end - - # FIXME: There is a potential race since we could be changing one - # non-nil listener to another - @listener = block - - if block_given? && @thread.nil? - @thread = Thread.new do - loop do - begin - o = get() - break if o == STOP - @listener.call(o) - rescue Qpid::Closed => e - @exc_listener.call(e) if @exc_listener - break - end - end - end - end - end - -end diff --git a/ruby/lib/qpid/session.rb b/ruby/lib/qpid/session.rb deleted file mode 100644 index d693b722c2..0000000000 --- a/ruby/lib/qpid/session.rb +++ /dev/null @@ -1,458 +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 'monitor' - -module Qpid - - class Session < Invoker - - def log; Qpid::logger["qpid.io.cmd"]; end - def msg; Qpid::logger["qpid.io.msg"]; end - - - class Exception < RuntimeError; end - class Closed < Qpid::Session::Exception; end - class Detached < Qpid::Session::Exception; end - - - INCOMPLETE = Object.new - - def self.client(*args) - return Qpid::Client(*args) - end - - def self.server(*args) - return Server(*args) - end - - attr_reader :name, :spec, :auto_sync, :timeout, :channel - attr_reader :results, :exceptions - attr_accessor :channel, :auto_sync, :send_id, :receiver, :sender - - # FIXME: Pass delegate through a block ? - def initialize(name, spec, kwargs = {}) - auto_sync = true - auto_sync = kwargs[:auto_sync] if kwargs.key?(:auto_sync) - timeout = kwargs[:timeout] || 10 - delegate = kwargs[:delegate] - - @name = name - @spec = spec - @auto_sync = auto_sync - @timeout = timeout - @invoke_lock = Monitor.new - @closing = false - @closed = false - - @cond_lock = Monitor.new - @condition = @cond_lock.new_cond - - @send_id = true - @receiver = Receiver.new(self) - @sender = Sender.new(self) - - @lock = Monitor.new - @incoming = {} - @results = {} - @exceptions = [] - - @assembly = nil - - @delegate = delegate.call(self) if delegate - - @ctl_seg = spec[:segment_type].enum[:control].value - @cmd_seg = spec[:segment_type].enum[:command].value - @hdr_seg = spec[:segment_type].enum[:header].value - @body_seg = spec[:segment_type].enum[:body].value - end - - def incoming(destination) - @lock.synchronize do - queue = @incoming[destination] - unless queue - queue = Incoming.new(self, destination) - @incoming[destination] = queue - end - return queue - end - end - - def error? - @exceptions.size > 0 - end - - def sync(timeout=nil) - if channel && Thread.current == channel.connection.thread - raise Qpid::Session::Exception, "deadlock detected" - end - unless @auto_sync - execution_sync(:sync => true) - end - last = @sender.next_id - 1 - @cond_lock.synchronize do - unless @condition.wait_for(timeout) { - @sender.completed.include?(last) || error? - } - raise Qpid::Timeout - end - end - if error? - raise Qpid::Session::Exception, @exceptions - end - end - - def close(timeout=nil) - @invoke_lock.synchronize do - @closing = true - channel.session_detach(name) - end - @cond_lock.synchronize do - unless @condition.wait_for(timeout) { @closed } - raise Qpid::Timeout - end - end - end - - def closed - @lock.synchronize do - return if @closed - - @results.each { |id, f| f.error(exceptions) } - @results.clear - - @incoming.values.each { |q| q.close(exceptions) } - @closed = true - @cond_lock.synchronize { @condition.signal } - end - end - - def resolve_method(name) - o = @spec.children[name] - case o - when Qpid::Spec010::Command - return invocation(:method, o) - when Qpid::Spec010::Struct - return invocation(:method, o) - when Qpid::Spec010::Domain - return invocation(:value, o.enum) unless o.enum.nil? - end - - matches = @spec.children.select { |x| - x.name.to_s.include?(name.to_s) - }.collect { |x| x.name.to_s }.sort - if matches.size == 0 - msg = nil - elsif matches.size == 1 - msg = "Did you mean #{matches[0]} ? " - else - msg = "Did you mean one of #{matches.join(",")} ? " - end - return invocation(:error, msg) - end - - def invoke(type, args) - # XXX - unless type.respond_to?(:track) - return type.create(*args) - end - @invoke_lock.synchronize do - return do_invoke(type, args) - end - end - - def do_invoke(type, args) - raise Qpid::Session::Closed if @closing - raise Qpid::Session::Detached unless channel - - # Clumsy simulation of Python's keyword args - kwargs = {} - if args.size > 0 && args[-1].is_a?(Hash) - if args.size > type.fields.size - kwargs = args.pop - elsif type.fields[args.size - 1].type != @spec[:map] - kwargs = args.pop - end - end - - if type.payload - if args.size == type.fields.size + 1 - message = args.pop - else - message = kwargs.delete(:message) # XXX Really ? - end - else - message = nil - end - - hdr = Qpid::struct(@spec[:header]) - hdr.sync = @auto_sync || kwargs.delete(:sync) - - cmd = type.create(*args.push(kwargs)) - sc = Qpid::StringCodec.new(@spec) - sc.write_command(hdr, cmd) - - seg = Segment.new(true, (message.nil? || - (message.headers.nil? && message.body.nil?)), - type.segment_type, type.track, @channel.id, sc.encoded) - - unless type.result.nil? - result = Future.new(exception=Exception) - @results[@sender.next_id] = result - end - emit(seg) - - log.debug("SENT %s %s %s" % [seg.id, hdr, cmd]) if log - - unless message.nil? - unless message.headers.nil? - sc = Qpid::StringCodec.new(@spec) - message.headers.each { |st| sc.write_struct32(st) } - - seg = Segment.new(false, message.body.nil?, @hdr_seg, - type.track, @channel.id, sc.encoded) - emit(seg) - end - unless message.body.nil? - seg = Segment.new(false, true, @body_seg, type.track, - @channel.id, message.body) - emit(seg) - end - msg.debug("SENT %s" % message) if msg - end - - if !type.result.nil? - return @auto_sync ? result.get(@timeout) : result - elsif @auto_sync - sync(@timeout) - end - end - - def received(seg) - @receiver.received(seg) - if seg.first_segment? - raise Qpid::Session::Exception unless @assembly.nil? - @assembly = [] - end - @assembly << seg - if seg.last_segment? - dispatch(@assembly) - @assembly = nil - end - end - - def dispatch(assembly) - hdr = nil - cmd = nil - header = nil - body = nil - assembly.each do |seg| - d = seg.decode(@spec) - case seg.type - when @cmd_seg - hdr, cmd = d - when @hdr_seg - header = d - when @body_seg - body = d - else - raise Qpid::Session::Exception - end - end - log.debug("RECV %s %s %s" % [cmd.id, hdr, cmd]) if log - - if cmd.st_type.payload - result = @delegate.send(cmd.st_type.name, cmd, header, body) - else - result = @delegate.send(cmd.st_type.name, cmd) - end - - unless cmd.st_type.result.nil? - execution_result(cmd.id, result) - end - - if result != INCOMPLETE - assembly.each do |seg| - @receiver.has_completed(seg) - # XXX: don't forget to obey sync for manual completion as well - if hdr.sync - @channel.session_completed(@receiver.completed) - end - end - end - end - - # Python calls this 'send', but that has a special meaning - # in Ruby, so we call it 'emit' - def emit(seg) - @sender.emit(seg) - end - - def signal - @cond_lock.synchronize { @condition.signal } - end - - def wait_for(timeout = nil, &block) - @cond_lock.synchronize { @condition.wait_for(timeout, &block) } - end - - def to_s - "<Session: #{name}, #{channel}>" - end - - class Receiver - - attr_reader :completed - attr_accessor :next_id, :next_offset - - def initialize(session) - @session = session - @next_id = nil - @next_offset = nil - @completed = Qpid::RangedSet.new() - end - - def received(seg) - if @next_id.nil? || @next_offset.nil? - raise Exception, "todo" - end - seg.id = @next_id - seg.offset = @next_offset - if seg.last_segment? - @next_id += 1 - @next_offset = 0 - else - @next_offset += seg.payload.size - end - end - - def has_completed(seg) - if seg.id.nil? - raise ArgumentError, "cannot complete unidentified segment" - end - if seg.last_segment? - @completed.add(seg.id) - end - end - - def known_completed(commands) - completed = Qpid::RangedSet.new() - @completed.ranges.each do |c| - unless commands.ranges.find { |kc| - kc.contains(c.lower) && kc.contains(c.upper) - } - completed.add_range(c) - end - end - @completed = completed - end - end - - class Sender - - def initialize(session) - @session = session - @next_id = 0.to_serial - @next_offset = 0 - @segments = [] - @completed = RangedSet.new() - end - - attr_reader :next_id, :completed - - def emit(seg) - seg.id = @next_id - seg.offset = @next_offset - if seg.last_segment? - @next_id += 1 - @next_offset = 0 - else - @next_offset += seg.payload.size - end - @segments << seg - if @session.send_id - @session.send_id = false - @session.channel.session_command_point(seg.id, seg.offset) - end - @session.channel.connection.write_segment(seg) - end - - def has_completed(commands) - @segments = @segments.reject { |seg| commands.include?(seg.id) } - commands.ranges.each do |range| - @completed.add(range.lower, range.upper) - end - end - end - - class Incoming < Qpid::Queue - - def initialize(session, destination) - super() - @session = session - @destination = destination - end - - def start - @session.message_credit_unit.choices.each do |unit| - @session.message_flow(@destination, unit.value, 0xFFFFFFFF) - end - end - - def stop - @session.message_cancel(@destination) - listen # Kill the listener - end - end - - class Delegate - - def initialize(session) - @session = session - end - - #XXX: do something with incoming accepts - def message_accept(ma) nil; end - - def execution_result(er) - future = @session.results.delete(er.command_id) - future.set(er.value) - end - - def execution_exception(ex) - @session.exceptions << ex - end - end - - class Client < Delegate - - def log ; Qpid::logger["qpid.io.msg"]; end - - def message_transfer(cmd, headers, body) - m = Qpid::Message.new(body) - m.headers = headers - m.id = cmd.id - messages = @session.incoming(cmd.destination) - messages.put(m) - log.debug("RECV %s" % m) if log - return INCOMPLETE - end - end - end -end diff --git a/ruby/lib/qpid/spec.rb b/ruby/lib/qpid/spec.rb deleted file mode 100644 index b3d70d019d..0000000000 --- a/ruby/lib/qpid/spec.rb +++ /dev/null @@ -1,183 +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 "set" -require "rexml/document" -require "qpid/fields" -require "qpid/traverse" - -module Qpid - module Spec - - include REXML - - class Container < Array - - def initialize() - @cache = {} - end - - def [](key) - return @cache[key] if @cache.include?(key) - value = do_lookup(key) - @cache[key] = value - return value - end - - def do_lookup(key) - case key - when String - return find {|x| x.name == key.intern()} - when Symbol - return find {|x| x.name == key} - else - return slice(key) - end - end - - def +(other) - copy = clone() - copy.concat(other) - return copy - end - - end - - class Reference - - fields(:name) - - def init(&block) - @resolver = block - end - - def resolve(*args) - @resolver.call(*args) - end - - end - - class Loader - - def initialize() - @stack = [] - end - - def container() - return Container.new() - end - - def load(obj) - case obj - when String - elem = @stack[-1] - result = container() - elem.elements.each(obj) {|e| - @index = result.size - result << load(e) - } - @index = nil - return result - else - elem = obj - @stack << elem - begin - result = send(:"load_#{elem.name}") - ensure - @stack.pop() - end - return result - end - end - - def element - @stack[-1] - end - - def text - element.text - end - - def attr(name, type = :string, default = nil, path = nil) - if path.nil? - elem = element - else - elem = nil - element.elements.each(path) {|elem|} - if elem.nil? - return default - end - end - - value = elem.attributes[name] - value = value.strip() unless value.nil? - if value.nil? - default - else - send(:"parse_#{type}", value) - end - end - - def parse_int(value) - if value.nil? - return nil - else - value.to_i(0) - end - end - - TRUE = ["yes", "true", "1"].to_set - FALSE = ["no", "false", "0", nil].to_set - - def parse_bool(value) - if TRUE.include?(value) - true - elsif FALSE.include?(value) - false - else - raise Exception.new("parse error, expecting boolean: #{value}") - end - end - - def parse_string(value) - value.to_s - end - - def parse_symbol(value) - value.intern() unless value.nil? - end - - REPLACE = {" " => "_", "-" => "_"} - KEYWORDS = {"global" => "global_", "return" => "return_"} - - def parse_name(value) - return if value.nil? - - REPLACE.each do |k, v| - value = value.gsub(k, v) - end - - value = KEYWORDS[value] if KEYWORDS.has_key? value - return value.intern() - end - - end - - end -end diff --git a/ruby/lib/qpid/spec010.rb b/ruby/lib/qpid/spec010.rb deleted file mode 100644 index 3e54115087..0000000000 --- a/ruby/lib/qpid/spec010.rb +++ /dev/null @@ -1,485 +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/spec" -require 'pathname' -require 'fileutils' - -module Qpid::Spec010 - - include Qpid::Spec - - # XXX: workaround for ruby bug/missfeature - Reference = Reference - Loader = Loader - - class Spec - - ENCODINGS = { - String => "str16", - Fixnum => "int64", - Bignum => "int64", - Float => "float", - NilClass => "void", - Array => "list", - Hash => "map" - } - - fields(:major, :minor, :port, :children) - - def init() - @controls = {} - @commands = {} - @structs = {} - @types = {} - children.each {|c| - case c - when Control - @controls[c.code] = c - when Command - @commands[c.code] = c - when Struct - @structs[c.code] = c - when Type - @types[c.code] = c unless c.code.nil? - end - } - end - - attr_reader :controls, :commands, :structs, :types - - def [](key) - return @children[key] - end - - def encoding(klass) - if ENCODINGS.has_key?(klass) - return self[ENCODINGS[klass]] - end - for base in klass.__bases__ - result = encoding(base) - return result unless result.nil? - end - end - - def inspect; "spec"; end - end - - class Constant - - fields(:name, :value) - - attr :parent, true - - end - - class Type - - fields(:name, :code, :fixed, :variable) - - attr :parent, true - - def present?(value) - if @fixed == 0 - return value - else - return !value.nil? - end - end - - def encode(codec, value) - codec.send("write_#{name}", value) - end - - def decode(codec) - return codec.send("read_#{name}") - end - - def inspect; name; end - - end - - class Domain < Type - - fields(:name, :type, :enum) - - attr :parent, true - - def encode(codec, value) - @type.encode(codec, value) - end - - def decode(codec) - return @type.decode(codec) - end - - end - - class Enum - fields(:choices) - - def [](choice) - case choice - when String - choice = choice.to_sym - return choices.find { |c| c.name == choice } - when Symbol - return choices.find { |c| c.name == choice } - else - return choices.find { |c| c.value == choice } - end - end - - def method_missing(name, *args) - raise ArgumentError.new("wrong number of arguments") unless args.empty? - return self[name].value - end - - end - - class Choice - fields(:name, :value) - end - - class Composite - - fields(:name, :code, :size, :pack, :fields) - - attr :parent, true - - # Python calls this 'new', but that has special meaning in Ruby - def create(*args) - return Qpid::struct(self, *args) - end - - def decode(codec) - codec.read_size(@size) - codec.read_uint16() unless @code.nil? - return Qpid::struct(self, self.decode_fields(codec)) - end - - def decode_fields(codec) - flags = 0 - pack.times {|i| flags |= (codec.read_uint8() << 8*i)} - - result = {} - - fields.each_index {|i| - f = @fields[i] - if flags & (0x1 << i) != 0 - result[f.name] = f.type.decode(codec) - else - result[f.name] = nil - end - } - - return result - end - - def encode(codec, value) - sc = Qpid::StringCodec.new(@spec) - sc.write_uint16(@code) unless @code.nil? - encode_fields(sc, value) - codec.write_size(@size, sc.encoded.size) - codec.write(sc.encoded) - end - - def encode_fields(codec, values) - # FIXME: This could be written cleaner using select - # instead of flags - flags = 0 - fields.each_index do |i| - f = fields[i] - flags |= (0x1 << i) if f.type.present?(values[f.name]) - end - - pack.times { |i| codec.write_uint8((flags >> 8*i) & 0xFF) } - - fields.each_index do |i| - f = fields[i] - f.type.encode(codec, values[f.name]) if flags & (0x1 << i) != 0 - end - end - - def inspect; name; end - - end - - class Field - - fields(:name, :type, :exceptions) - - def default() - return nil - end - - end - - class Struct < Composite - - def present?(value) - return !value.nil? - end - - end - - class Action < Composite; end - - class Control < Action - - def segment_type - @parent[:segment_type].enum[:control].value - end - - def track - @parent[:track].enum[:control].value - end - - end - - class Command < Action - - attr_accessor :payload, :result - - def segment_type - @parent["segment_type"].enum["command"].value - end - - def track - @parent["track"].enum["command"].value - end - - end - - class Doc - fields(:type, :title, :text) - end - - class Loader010 < Loader - - def initialize() - super() - end - - def klass - cls = element - until cls.nil? - break if cls.name == "class" - cls = cls.parent - end - return cls - end - - def scope - if element.name == "struct" - return nil - else - return class_name - end - end - - def class_name - cls = klass - if cls.nil? - return nil - else - return parse_name(cls.attributes["name"].strip) - end - end - - def class_code - cls = klass - if cls.nil? - return 0 - else - return parse_int(cls.attributes["code"].strip) - end - end - - def parse_decl(value) - name = parse_name(value) - - s = scope - if s.nil? - return name - else - return :"#{s}_#{name}" - end - end - - def parse_code(value) - c = parse_int(value) - if c.nil? - return nil - else - return c | (class_code << 8) - end - end - - def parse_type(value) - name = parse_name(value.sub(".", "_")) - cls = class_name - return Reference.new {|spec| - candidates = [name] - candidates << :"#{cls}_#{name}" unless cls.nil? - for c in candidates - child = spec[c] - break unless child.nil? - end - if child.nil? - raise Exception.new("unresolved type: #{name}") - else - child - end -} - end - - def load_amqp() - children = nil - - for s in ["constant", "type", "domain", "struct", "control", - "command"] - ch = load(s) - if children.nil? - children = ch - else - children += ch - end - children += load("class/#{s}") - end - children += load("class/command/result/struct") - Spec.new(attr("major", :int), attr("minor", :int), attr("port", :int), - children) - end - - def load_constant() - Constant.new(attr("name", :decl), attr("value", :int)) - end - - def load_type() - Type.new(attr("name", :decl), attr("code", :code), - attr("fixed-width", :int), attr("variable-width", :int)) - end - - def load_domain() - Domain.new(attr("name", :decl), attr("type", :type), load("enum").first) - end - - def load_enum() - Enum.new(load("choice")) - end - - def load_choice() - Choice.new(attr("name", :name), attr("value", :int)) - end - - def load_field() - Field.new(attr("name", :name), attr("type", :type)) - end - - def load_struct() - Struct.new(attr("name", :decl), attr("code", :code), attr("size", :int), - attr("pack", :int), load("field")) - end - - def load_action(cls) - cls.new(attr("name", :decl), attr("code", :code), 0, 2, load("field")) - end - - def load_control() - load_action(Control) - end - - def load_command() - result = attr("type", :type, nil, "result") - result = attr("name", :type, nil, "result/struct") if result.nil? - segs = load("segments") - cmd = load_action(Command) - cmd.result = result - cmd.payload = !segs.empty? - return cmd - end - - def load_result() - true - end - - def load_segments() - true - end - - end - - def self.spec_cache(specfile) - File::join(File::dirname(__FILE__), "spec_cache", - File::basename(specfile, ".xml") + ".rb_marshal") - end - - # XXX: could be shared - def self.load(spec = nil) - return spec if spec.is_a?(Qpid::Spec010::Spec) - if spec.nil? - # FIXME: Need to add a packaging setup in here so we know where - # the installed spec is going to be. - specfile = nil - if ENV['AMQP_SPEC'] - specfile = ENV['AMQP_SPEC'] - else - require "qpid/config" - specfile = Qpid::Config.amqp_spec - end - else - specfile = spec - end - - specfile_cache = spec_cache(specfile) - # FIXME: Check that cache is newer than specfile - if File::exist?(specfile_cache) - begin - spec = File::open(specfile_cache, "r") do |f| - Marshal::load(f) - end - return spec - rescue - # Ignore, will load from XML - end - end - - doc = File::open(specfile, "r") { |f| Document.new(f) } - spec = Loader010.new().load(doc.root) - spec.traverse! do |o| - if o.is_a?(Reference) - o.resolve(spec) - else - o - end - end - - spec.children.each { |c| c.parent = spec } - - begin - FileUtils::mkdir_p(File::dirname(specfile_cache)) - File::open(specfile_cache, "w") { |f| Marshal::dump(spec, f) } - rescue - # Ignore, we are fine without the cached spec - end - return spec - end - -end diff --git a/ruby/lib/qpid/spec08.rb b/ruby/lib/qpid/spec08.rb deleted file mode 100644 index 902c05c297..0000000000 --- a/ruby/lib/qpid/spec08.rb +++ /dev/null @@ -1,190 +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/spec" - -module Qpid08 - - module Spec - - include Qpid::Spec - - # XXX: workaround for ruby bug/missfeature - Reference = Reference - - class Root - fields(:major, :minor, :classes, :constants, :domains) - - def find_method(name) - classes.each do |c| - c.methods.each do |m| - if name == m.qname - return m - end - end - end - - return nil - end - end - - class Constant - fields(:name, :id, :type, :docs) - end - - class Domain - fields(:name, :type) - end - - class Class - fields(:name, :id, :handler, :fields, :methods, :docs) - end - - class Method - fields(:name, :id, :content?, :responses, :synchronous?, :fields, - :docs) - - def init() - @response = false - end - - attr :parent, true - - def response?; @response end - def response=(b); @response = b end - - def qname - :"#{parent.name}_#{name}" - end - end - - class Field - fields(:name, :id, :type, :docs) - - def default - case type - when :bit then false - when :octet, :short, :long, :longlong then 0 - when :shortstr, :longstr then "" - when :table then {} - end - end - end - - class Doc - fields(:type, :text) - end - - class Container08 < Container - def do_lookup(key) - case key - when Integer - return find {|x| x.id == key} - else - return super(key) - end - end - end - - class Loader08 < Loader - - def container() - return Container08.new() - end - - def load_amqp() - Root.new(attr("major", :int), attr("minor", :int), load("class"), - load("constant"), load("domain")) - end - - def load_class() - Class.new(attr("name", :name), attr("index", :int), attr("handler", :name), - load("field"), load("method"), load("doc")) - end - - def load_method() - Method.new(attr("name", :name), attr("index", :int), - attr("content", :bool), load("response"), - attr("synchronous", :bool), load("field"), load("docs")) - end - - def load_response() - name = attr("name", :name) - Reference.new {|spec, klass| - response = klass.methods[name] - if response.nil? - raise Exception.new("no such method: #{name}") - end - response - } - end - - def load_field() - type = attr("type", :name) - if type.nil? - domain = attr("domain", :name) - type = Reference.new {|spec, klass| - spec.domains[domain].type - } - end - Field.new(attr("name", :name), @index, type, load("docs")) - end - - def load_constant() - Constant.new(attr("name", :name), attr("value", :int), attr("class", :name), - load("doc")) - end - - def load_domain() - Domain.new(attr("name", :name), attr("type", :name)) - end - - def load_doc() - Doc.new(attr("type", :symbol), text) - end - - end - - def self.load(spec) - case spec - when String - spec = File.new(spec) - end - doc = Document.new(spec) - spec = Loader08.new().load(doc.root) - spec.classes.each do |klass| - klass.traverse! do |o| - case o - when Reference - o.resolve(spec, klass) - else - o - end - end - klass.methods.each do |m| - m.parent = klass - m.responses.each do |r| - r.response = true - end - end - end - return spec - end - end -end diff --git a/ruby/lib/qpid/specs/amqp.0-10-qpid-errata.xml b/ruby/lib/qpid/specs/amqp.0-10-qpid-errata.xml deleted file mode 100644 index 365928ea4e..0000000000 --- a/ruby/lib/qpid/specs/amqp.0-10-qpid-errata.xml +++ /dev/null @@ -1,6654 +0,0 @@ -<?xml version="1.0"?> - -<!-- - Copyright Notice - ================ - (c) Copyright Cisco Systems, Credit Suisse, Deutsche Borse Systems, Envoy Technologies, Inc., - Goldman Sachs, IONA Technologies PLC, iMatix Corporation sprl.,JPMorgan Chase Bank Inc. N.A, - Novell, Rabbit Technologies Ltd., Red Hat, Inc., TWIST Process Innovations ltd, and 29West Inc. - 2006, 2007. All rights reserved. - - License - ======= - - Cisco Systems, Credit Suisse, Deutsche Borse Systems, Envoy Technologies, Inc.,Goldman Sachs, - IONA Technologies PLC, iMatix Corporation sprl.,JPMorgan Chase Bank Inc. N.A, Novell, Rabbit - Technologies Ltd., Red Hat, Inc., TWIST Process Innovations ltd, and 29West Inc. (collectively, - the "Authors") each hereby grants to you a worldwide, perpetual, royalty-free, nontransferable, - nonexclusive license to (i) copy, display, distribute and implement the Advanced Messaging Queue - Protocol ("AMQP") Specification and (ii) the Licensed Claims that are held by the Authors, all for - the purpose of implementing the Advanced Messaging Queue Protocol Specification. Your license and - any rights under this Agreement will terminate immediately without notice from any Author if you - bring any claim, suit, demand, or action related to the Advanced Messaging Queue Protocol - Specification against any Author. Upon termination, you shall destroy all copies of the Advanced - Messaging Queue Protocol Specification in your possession or control. - - As used hereunder, "Licensed Claims" means those claims of a patent or patent application, - throughout the world, excluding design patents and design registrations, owned or controlled, or - that can be sublicensed without fee and in compliance with the requirements of this Agreement, by - an Author or its affiliates now or at any future time and which would necessarily be infringed by - implementation of the Advanced Messaging Queue Protocol Specification. A claim is necessarily - infringed hereunder only when it is not possible to avoid infringing it because there is no - plausible non-infringing alternative for implementing the required portions of the Advanced - Messaging Queue Protocol Specification. Notwithstanding the foregoing, Licensed Claims shall not - include any claims other than as set forth above even if contained in the same patent as Licensed - Claims; or that read solely on any implementations of any portion of the Advanced Messaging Queue - Protocol Specification that are not required by the Advanced Messaging Queue Protocol - Specification, or that, if licensed, would require a payment of royalties by the licensor to - unaffiliated third parties. Moreover, Licensed Claims shall not include (i) any enabling - technologies that may be necessary to make or use any Licensed Product but are not themselves - expressly set forth in the Advanced Messaging Queue Protocol Specification (e.g., semiconductor - manufacturing technology, compiler technology, object oriented technology, networking technology, - operating system technology, and the like); or (ii) the implementation of other published - standards developed elsewhere and merely referred to in the body of the Advanced Messaging Queue - Protocol Specification, or (iii) any Licensed Product and any combinations thereof the purpose or - function of which is not required for compliance with the Advanced Messaging Queue Protocol - Specification. For purposes of this definition, the Advanced Messaging Queue Protocol - Specification shall be deemed to include both architectural and interconnection requirements - essential for interoperability and may also include supporting source code artifacts where such - architectural, interconnection requirements and source code artifacts are expressly identified as - being required or documentation to achieve compliance with the Advanced Messaging Queue Protocol - Specification. - - As used hereunder, "Licensed Products" means only those specific portions of products (hardware, - software or combinations thereof) that implement and are compliant with all relevant portions of - the Advanced Messaging Queue Protocol Specification. - - The following disclaimers, which you hereby also acknowledge as to any use you may make of the - Advanced Messaging Queue Protocol Specification: - - THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS," AND THE AUTHORS MAKE NO - REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS - OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE - IMPLEMENTATION OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD - PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. - - THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL - DAMAGES ARISING OUT OF OR RELATING TO ANY USE, IMPLEMENTATION OR DISTRIBUTION OF THE ADVANCED - MESSAGING QUEUE PROTOCOL SPECIFICATION. - - The name and trademarks of the Authors may NOT be used in any manner, including advertising or - publicity pertaining to the Advanced Messaging Queue Protocol Specification or its contents - without specific, written prior permission. Title to copyright in the Advanced Messaging Queue - Protocol Specification will at all times remain with the Authors. - - No other rights are granted by implication, estoppel or otherwise. - - Upon termination of your license or rights under this Agreement, you shall destroy all copies of - the Advanced Messaging Queue Protocol Specification in your possession or control. - - Trademarks - ========== - "JPMorgan", "JPMorgan Chase", "Chase", the JPMorgan Chase logo and the Octagon Symbol are - trademarks of JPMorgan Chase & Co. - - IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl. - - IONA, IONA Technologies, and the IONA logos are trademarks of IONA Technologies PLC and/or its - subsidiaries. - - LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered trademarks of Red Hat, - Inc. in the US and other countries. - - Java, all Java-based trademarks and OpenOffice.org are trademarks of Sun Microsystems, Inc. in the - United States, other countries, or both. - - Other company, product, or service names may be trademarks or service marks of others. - - Links to full AMQP specification: - ================================= - http://www.envoytech.org/spec/amq/ - http://www.iona.com/opensource/amqp/ - http://www.redhat.com/solutions/specifications/amqp/ - http://www.twiststandards.org/tiki-index.php?page=AMQ - http://www.imatix.com/amqp ---> - -<!-- - XML Notes - ========= - - We use entities to indicate repetition; attributes to indicate properties. - - We use the "name" attribute as an identifier, usually within the context of the surrounding - entities. - - We use hyphens (minus char '-') to seperate words in names. - - We do not enforce any particular validation mechanism but we support all mechanisms. The protocol - definition conforms to a formal grammar that is published seperately in several technologies. - ---> - -<!DOCTYPE amqp SYSTEM "amqp.0-10.dtd"> - -<amqp xmlns="http://www.amqp.org/schema/amqp.xsd" - major="0" minor="10" port="5672"> - - <!-- - ====================== == type definitions == ====================== - --> - - <!-- - 0x00 - 0x0f: Fixed width, 1 octet - --> - - <type name="bin8" code="0x00" fixed-width="1" label="octet of unspecified encoding"> - <doc> - The bin8 type consists of exactly one octet of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET - +----------+ - | bin8 | - +----------+ - </doc> - - <doc type="bnf"> - bin8 = OCTET - </doc> - </type> - - <type name="int8" code="0x01" fixed-width="1" label="8-bit signed integral value (-128 - 127)"> - <doc> - The int8 type is a signed integral value encoded using an 8-bit two's complement - representation. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET - +----------+ - | int8 | - +----------+ - </doc> - - <doc type="bnf"> - int8 = OCTET - </doc> - </type> - - <type name="uint8" code="0x02" fixed-width="1" label="8-bit unsigned integral value (0 - 255)"> - <doc> - The uint8 type is an 8-bit unsigned integral value. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET - +---------+ - | uint8 | - +---------+ - </doc> - - <doc type="bnf"> - uint8 = OCTET - </doc> - </type> - - <type name="char" code="0x04" fixed-width="1" label="an iso-8859-15 character"> - <doc> - The char type encodes a single character from the iso-8859-15 character set. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET - +----------+ - | char | - +----------+ - </doc> - - <doc type="bnf"> - char = OCTET - </doc> - </type> - - <type name="boolean" code="0x08" fixed-width="1" - label="boolean value (zero represents false, nonzero represents true)"> - <doc> - The boolean type is a single octet that encodes a true or false value. If the octet is zero, - then the boolean is false. Any other value represents true. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET - +---------+ - | boolean | - +---------+ - </doc> - - <doc type="bnf"> - boolean = OCTET - </doc> - </type> - - <!-- - 0x10 - 0x1f: Fixed width, 2 octets - --> - - <type name="bin16" code="0x10" fixed-width="2" label="two octets of unspecified binary encoding"> - <doc> - The bin16 type consists of two consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET - +-----------+-----------+ - | octet-one | octet-two | - +-----------+-----------+ - </doc> - - <doc type="bnf"> - bin16 = 2 OCTET - </doc> - </type> - - <type name="int16" code="0x11" fixed-width="2" label="16-bit signed integral value"> - <doc> - The int16 type is a signed integral value encoded using a 16-bit two's complement - representation in network byte order. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET - +-----------+----------+ - | high-byte | low-byte | - +-----------+----------+ - </doc> - - <doc type="bnf"> - int16 = high-byte low-byte - high-byte = OCTET - low-byte = OCTET - </doc> - </type> - - <type name="uint16" code="0x12" fixed-width="2" label="16-bit unsigned integer"> - <doc> - The uint16 type is a 16-bit unsigned integral value encoded in network byte order. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET - +-----------+----------+ - | high-byte | low-byte | - +-----------+----------+ - </doc> - - <doc type="bnf"> - uint16 = high-byte low-byte - high-byte = OCTET - low-byte = OCTET - </doc> - </type> - - <!-- - 0x20 - 0x2f: Fixed width, 4 octets - --> - - <type name="bin32" code="0x20" fixed-width="4" label="four octets of unspecified binary encoding"> - <doc> - The bin32 type consists of 4 consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-------------+------------+ - | octet-one | octet-two | octet-three | octet-four | - +-----------+-----------+-------------+------------+ - </doc> - - <doc type="bnf"> - bin32 = 4 OCTET - </doc> - </type> - - <type name="int32" code="0x21" fixed-width="4" label="32-bit signed integral value"> - <doc> - The int32 type is a signed integral value encoded using a 32-bit two's complement - representation in network byte order. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+------------+----------+----------+ - | byte-four | byte-three | byte-two | byte-one | - +-----------+------------+----------+----------+ - MSB LSB - </doc> - - <doc type="bnf"> - int32 = byte-four byte-three byte-two byte-one - byte-four = OCTET ; most significant byte (MSB) - byte-three = OCTET - byte-two = OCTET - byte-one = OCTET ; least significant byte (LSB) - </doc> - </type> - - <type name="uint32" code="0x22" fixed-width="4" label="32-bit unsigned integral value"> - <doc> - The uint32 type is a 32-bit unsigned integral value encoded in network byte order. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+------------+----------+----------+ - | byte-four | byte-three | byte-two | byte-one | - +-----------+------------+----------+----------+ - MSB LSB - </doc> - - <doc type="bnf"> - uint32 = byte-four byte-three byte-two byte-one - byte-four = OCTET ; most significant byte (MSB) - byte-three = OCTET - byte-two = OCTET - byte-one = OCTET ; least significant byte (LSB) - </doc> - </type> - - <type name="float" code="0x23" fixed-width="4" - label="single precision IEEE 754 32-bit floating point"> - <doc> - The float type encodes a single precision 32-bit floating point number. The format and - operations are defined by the IEEE 754 standard for 32-bit floating point numbers. - </doc> - - <doc type="picture" title="Wire Format"> - 4 OCTETs - +-----------------------+ - | float | - +-----------------------+ - IEEE 754 32-bit float - </doc> - - <doc type="bnf"> - float = 4 OCTET ; IEEE 754 32-bit floating point number - </doc> - </type> - - <type name="char-utf32" code="0x27" fixed-width="4" - label="single unicode character in UTF-32 encoding"> - <doc> - The char-utf32 type consists of a single unicode character in the UTF-32 encoding. - </doc> - - <doc type="picture" title="Wire Format"> - 4 OCTETs - +------------------+ - | char-utf32 | - +------------------+ - UTF-32 character - </doc> - - <doc type="bnf"> - char-utf32 = 4 OCTET ; single UTF-32 character - </doc> - </type> - - <type name="sequence-no" fixed-width="4" label="serial number defined in RFC-1982"> - <doc> - The sequence-no type encodes, in network byte order, a serial number as defined in RFC-1982. - The arithmetic, operators, and ranges for numbers of this type are defined by RFC-1982. - </doc> - - <doc type="picture" title="Wire Format"> - 4 OCTETs - +------------------------+ - | sequence-no | - +------------------------+ - RFC-1982 serial number - </doc> - - <doc type="bnf"> - sequence-no = 4 OCTET ; RFC-1982 serial number - </doc> - </type> - - <!-- - 0x30 - 0x3f: Fixed width types - 8 octets - --> - - <type name="bin64" code="0x30" fixed-width="8" - label="eight octets of unspecified binary encoding"> - <doc> - The bin64 type consists of eight consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-----+-------------+-------------+ - | octet-one | octet-two | ... | octet-seven | octet-eight | - +-----------+-----------+-----+-------------+-------------+ - </doc> - - <doc type="bnf"> - bin64 = 8 OCTET - </doc> - </type> - - <type name="int64" code="0x31" fixed-width="8" label="64-bit signed integral value"> - <doc> - The int64 type is a signed integral value encoded using a 64-bit two's complement - representation in network byte order. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +------------+------------+-----+----------+----------+ - | byte-eight | byte-seven | ... | byte-two | byte-one | - +------------+------------+-----+----------+----------+ - MSB LSB - </doc> - - <doc type="bnf"> - int64 = byte-eight byte-seven byte-six byte-five - byte-four byte-three byte-two byte-one - byte-eight = 1 OCTET ; most significant byte (MSB) - byte-seven = 1 OCTET - byte-six = 1 OCTET - byte-five = 1 OCTET - byte-four = 1 OCTET - byte-three = 1 OCTET - byte-two = 1 OCTET - byte-one = 1 OCTET ; least significant byte (LSB) - </doc> - </type> - - <type name="uint64" code="0x32" fixed-width="8" label="64-bit unsigned integral value"> - <doc> - The uint64 type is a 64-bit unsigned integral value encoded in network byte order. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +------------+------------+-----+----------+----------+ - | byte-eight | byte-seven | ... | byte-two | byte-one | - +------------+------------+-----+----------+----------+ - MSB LSB - </doc> - - <doc type="bnf"> - uint64 = byte-eight byte-seven byte-six byte-five - byte-four byte-three byte-two byte-one - byte-eight = 1 OCTET ; most significant byte (MSB) - byte-seven = 1 OCTET - byte-six = 1 OCTET - byte-five = 1 OCTET - byte-four = 1 OCTET - byte-three = 1 OCTET - byte-two = 1 OCTET - byte-one = 1 OCTET ; least significant byte (LSB) - </doc> - </type> - - <type name="double" code="0x33" fixed-width="8" label="double precision IEEE 754 floating point"> - <doc> - The double type encodes a double precision 64-bit floating point number. The format and - operations are defined by the IEEE 754 standard for 64-bit double precision floating point - numbers. - </doc> - - <doc type="picture" title="Wire Format"> - 8 OCTETs - +-----------------------+ - | double | - +-----------------------+ - IEEE 754 64-bit float - </doc> - - <doc type="bnf"> - double = 8 OCTET ; double precision IEEE 754 floating point number - </doc> - </type> - - <type name="datetime" code="0x38" fixed-width="8" label="datetime in 64 bit POSIX time_t format"> - <doc> - The datetime type encodes a date and time using the 64 bit POSIX time_t format. - </doc> - - <doc type="picture" title="Wire Format"> - 8 OCTETs - +---------------------+ - | datetime | - +---------------------+ - posix time_t format - </doc> - - <doc type="bnf"> - datetime = 8 OCTET ; 64 bit posix time_t format - </doc> - </type> - - <!-- - 0x40 - 0x4f: Fixed width types - 16 octets - --> - - <type name="bin128" code="0x40" fixed-width="16" - label="sixteen octets of unspecified binary encoding"> - <doc> - The bin128 type consists of 16 consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-----+---------------+---------------+ - | octet-one | octet-two | ... | octet-fifteen | octet-sixteen | - +-----------+-----------+-----+---------------+---------------+ - </doc> - - <doc type="bnf"> - bin128 = 16 OCTET - </doc> - </type> - - <type name="uuid" code="0x48" fixed-width="16" label="UUID (RFC-4122 section 4.1.2) - 16 octets"> - <doc> - The uuid type encodes a universally unique id as defined by RFC-4122. The format and - operations for this type can be found in section 4.1.2 of RFC-4122. - </doc> - - <doc type="picture" title="Wire Format"> - 16 OCTETs - +---------------+ - | uuid | - +---------------+ - RFC-4122 UUID - </doc> - - <doc type="bnf"> - uuid = 16 OCTET ; RFC-4122 section 4.1.2 - </doc> - </type> - - <!-- - 0x50 - 0x5f: Fixed width types - 32 octets - --> - - <type name="bin256" code="0x50" fixed-width="32" - label="thirty two octets of unspecified binary encoding"> - <doc> - The bin256 type consists of thirty two consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-----+------------------+------------------+ - | octet-one | octet-two | ... | octet-thirty-one | octet-thirty-two | - +-----------+-----------+-----+------------------+------------------+ - </doc> - - <doc type="bnf"> - bin256 = 32 OCTET - </doc> - </type> - - <!-- - 0x60 - 0x6f: Fixed width types - 64 octets - --> - - <type name="bin512" code="0x60" fixed-width="64" - label="sixty four octets of unspecified binary encoding"> - <doc> - The bin512 type consists of sixty four consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-----+-------------------+------------------+ - | octet-one | octet-two | ... | octet-sixty-three | octet-sixty-four | - +-----------+-----------+-----+-------------------+------------------+ - </doc> - - <doc type="bnf"> - bin512 = 64 OCTET - </doc> - </type> - - <!-- - 0x70 - 0x7f: Fixed width types - 128 octets - --> - - <type name="bin1024" code="0x70" fixed-width="128" - label="one hundred and twenty eight octets of unspecified binary encoding"> - <doc> - The bin1024 type consists of one hundred and twenty eight octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-----+------------------------+------------------------+ - | octet-one | octet-two | ... | octet-one-twenty-seven | octet-one-twenty-eight | - +-----------+-----------+-----+------------------------+------------------------+ - </doc> - - <doc type="bnf"> - bin1024 = 128 OCTET - </doc> - </type> - - <!-- - 0x80 - 0x8f: Variable length - one byte length field (up to 255 octets) - --> - - <type name="vbin8" code="0x80" variable-width="1" label="up to 255 octets of opaque binary data"> - <doc> - The vbin8 type encodes up to 255 octets of opaque binary data. The number of octets is first - encoded as an 8-bit unsigned integral value. This is followed by the actual data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET size OCTETs - +---------+-------------+ - | size | octets | - +---------+-------------+ - uint8 - </doc> - - <doc type="bnf"> - vbin8 = size octets - size = uint8 - octets = 0*255 OCTET ; size OCTETs - </doc> - </type> - - <type name="str8-latin" code="0x84" variable-width="1" label="up to 255 iso-8859-15 characters"> - <doc> - The str8-latin type encodes up to 255 octets of iso-8859-15 characters. The number of octets - is first encoded as an 8-bit unsigned integral value. This is followed by the actual - characters. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET size OCTETs - +---------+------------------------+ - | size | characters | - +---------+------------------------+ - uint16 iso-8859-15 characters - </doc> - - <doc type="bnf"> - str8-latin = size characters - size = uint8 - characters = 0*255 OCTET ; size OCTETs - </doc> - </type> - - <type name="str8" code="0x85" variable-width="1" label="up to 255 octets worth of UTF-8 unicode"> - <doc> - The str8 type encodes up to 255 octets worth of UTF-8 unicode. The number of octets of unicode - is first encoded as an 8-bit unsigned integral value. This is followed by the actual UTF-8 - unicode. Note that the encoded size refers to the number of octets of unicode, not necessarily - the number of characters since the UTF-8 unicode may include multi-byte character sequences. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET size OCTETs - +---------+--------------+ - | size | utf8-unicode | - +---------+--------------+ - uint8 - </doc> - - <doc type="bnf"> - str8 = size utf8-unicode - size = uint8 - utf8-unicode = 0*255 OCTET ; size OCTETs - </doc> - </type> - - <type name="str8-utf16" code="0x86" variable-width="1" - label="up to 255 octets worth of UTF-16 unicode"> - <doc> - The str8-utf16 type encodes up to 255 octets worth of UTF-16 unicode. The number of octets of - unicode is first encoded as an 8-bit unsigned integral value. This is followed by the actual - UTF-16 unicode. Note that the encoded size refers to the number of octets of unicode, not the - number of characters since the UTF-16 unicode will include at least two octets per unicode - character. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET size OCTETs - +---------+---------------+ - | size | utf16-unicode | - +---------+---------------+ - uint8 - </doc> - - <doc type="bnf"> - str8-utf16 = size utf16-unicode - size = uint8 - utf16-unicode = 0*255 OCTET ; size OCTETs - </doc> - </type> - - <!-- - 0x90 - 0x9f: Variable length types - two byte length field (up to 65535 octets) - --> - - <type name="vbin16" code="0x90" variable-width="2" - label="up to 65535 octets of opaque binary data"> - <doc> - The vbin16 type encodes up to 65535 octets of opaque binary data. The number of octets is - first encoded as a 16-bit unsigned integral value in network byte order. This is followed by - the actual data. - </doc> - - <doc type="picture" title="Wire Format"> - 2 OCTETs size OCTETs - +----------+-------------+ - | size | octets | - +----------+-------------+ - uint16 - </doc> - - <doc type="bnf"> - vbin16 = size octets - size = uint16 - octets = 0*65535 OCTET ; size OCTETs - </doc> - </type> - - <type name="str16-latin" code="0x94" variable-width="2" - label="up to 65535 iso-8859-15 characters"> - <doc> - The str16-latin type encodes up to 65535 octets of is-8859-15 characters. The number of octets - is first encoded as a 16-bit unsigned integral value in network byte order. This is followed - by the actual characters. - </doc> - - <doc type="picture" title="Wire Format"> - 2 OCTETs size OCTETs - +----------+------------------------+ - | size | characters | - +----------+------------------------+ - uint16 iso-8859-15 characters - </doc> - - <doc type="bnf"> - str16-latin = size characters - size = uint16 - characters = 0*65535 OCTET ; size OCTETs - </doc> - </type> - - <type name="str16" code="0x95" variable-width="2" - label="up to 65535 octets worth of UTF-8 unicode"> - <doc> - The str16 type encodes up to 65535 octets worth of UTF-8 unicode. The number of octets is - first encoded as a 16-bit unsigned integral value in network byte order. This is followed by - the actual UTF-8 unicode. Note that the encoded size refers to the number of octets of - unicode, not necessarily the number of unicode characters since the UTF-8 unicode may include - multi-byte character sequences. - </doc> - - <doc type="picture" title="Wire Format"> - 2 OCTETs size OCTETs - +----------+--------------+ - | size | utf8-unicode | - +----------+--------------+ - uint16 - </doc> - - <doc type="bnf"> - str16 = size utf8-unicode - size = uint16 - utf8-unicode = 0*65535 OCTET ; size OCTETs - </doc> - </type> - - <type name="str16-utf16" code="0x96" variable-width="2" - label="up to 65535 octets worth of UTF-16 unicode"> - <doc> - The str16-utf16 type encodes up to 65535 octets worth of UTF-16 unicode. The number of octets - is first encoded as a 16-bit unsigned integral value in network byte order. This is followed - by the actual UTF-16 unicode. Note that the encoded size refers to the number of octets of - unicode, not the number of unicode characters since the UTF-16 unicode will include at least - two octets per unicode character. - </doc> - - <doc type="picture" title="Wire Format"> - 2 OCTETs size OCTETs - +----------+---------------+ - | size | utf16-unicode | - +----------+---------------+ - uint16 - </doc> - - <doc type="bnf"> - str16-utf16 = size utf16-unicode - size = uint16 - utf16-unicode = 0*65535 OCTET ; size OCTETs - </doc> - </type> - - <type name="byte-ranges" variable-width="2" label="byte ranges within a 64-bit payload"> - <doc> - The byte-ranges type encodes up to 65535 octets worth of non-overlapping, non-touching, - ascending byte ranges within a 64-bit sequence of bytes. Each range is represented as an - inclusive lower and upper bound that identifies all the byte offsets included within a given - range. - </doc> - - <doc> - The number of octets of data is first encoded as a 16-bit unsigned integral value in network - byte order. This is then followed by the encoded representation of the ranges included in the - set. These MUST be encoded in ascending order, and any two ranges included in a given set MUST - NOT include overlapping or touching byte offsets. - </doc> - - <doc> - Each range is encoded as a pair of 64-bit unsigned integral values in network byte order - respectively representing the lower and upper bounds for that range. Note that because each - range is exactly 16 octets, the size in octets of the encoded ranges will always be 16 times - the number of ranges in the set. - </doc> - - <doc type="picture" title="Wire Format"> - +----= size OCTETs =----+ - | | - 2 OCTETs | 16 OCTETs | - +----------+-----+-----------+-----+ - | size | .../| range |\... | - +----------+---/ +-----------+ \---+ - uint16 / / \ \ - / / \ \ - / 8 OCTETs 8 OCTETs \ - +-----------+-----------+ - | lower | upper | - +-----------+-----------+ - uint64 uint64 - </doc> - - <doc type="bnf"> - byte-ranges = size *range - size = uint16 - range = lower upper - lower = uint64 - upper = uint64 - </doc> - </type> - - <type name="sequence-set" variable-width="2" label="ranged set representation"> - <doc> - The sequence-set type is a set of pairs of RFC-1982 numbers representing a discontinuous range - within an RFC-1982 sequence. Each pair represents a closed interval within the list. - </doc> - - <doc> - Sequence-sets can be represented as lists of pairs of positive 32-bit numbers, each pair - representing a closed interval that does not overlap or touch with any other interval in the - list. For example, a set containing words 0, 1, 2, 5, 6, and 15 can be represented: - </doc> - - <doc type="picture"> - [(0, 2), (5, 6), (15, 15)] - </doc> - - <doc> - 1) The list-of-pairs representation is sorted ascending (as defined by RFC 1982 - (http://www.ietf.org/rfc/rfc1982.txt) ) by the first elements of each pair. - </doc> - - <doc> - 2) The list-of-pairs is flattened into a list-of-words. - </doc> - - <doc> - 3) Each word in the list is packed into ascending locations in memory with network byte - ordering. - </doc> - - <doc> - 4) The size in bytes, represented as a 16-bit network-byte-order unsigned value, is prepended. - </doc> - - <doc> - For instance, the example from above would be encoded: - </doc> - - <doc type="picture"> - [(0, 2), (5, 6), (15, 15)] -- already sorted. - [0, 2, 5, 6, 15, 15] -- flattened. - 000000000000000200000005000000060000000F0000000F -- bytes in hex - 0018000000000000000200000005000000060000000F0000000F -- bytes in hex, - length (24) prepended - </doc> - - <doc type="picture" title="Wire Format"> - +----= size OCTETs =----+ - | | - 2 OCTETs | 8 OCTETs | - +----------+-----+-----------+-----+ - | size | .../| range |\... | - +----------+---/ +-----------+ \---+ - uint16 / / \ \ - / / \ \ - / / \ \ - / / \ \ - / 4 OCTETs 4 OCTETs \ - +-------------+-------------+ - | lower | upper | - +-------------+-------------+ - sequence-no sequence-no - </doc> - - <doc type="bnf"> - sequence-set = size *range - size = uint16 ; length of variable portion in bytes - - range = lower upper ; inclusive - lower = sequence-no - upper = sequence-no - </doc> - </type> - - <!-- - 0xa0 - 0xaf: Variable length types - four byte length field (up to 4294967295 octets) - --> - - <type name="vbin32" code="0xa0" variable-width="4" - label="up to 4294967295 octets of opaque binary data"> - <doc> - The vbin32 type encodes up to 4294967295 octets of opaque binary data. The number of octets is - first encoded as a 32-bit unsigned integral value in network byte order. This is followed by - the actual data. - </doc> - - <doc type="picture" title="Wire Format"> - 4 OCTETs size OCTETs - +----------+-------------+ - | size | octets | - +----------+-------------+ - uint32 - </doc> - - <doc type="bnf"> - vbin32 = size octets - size = uint32 - octets = 0*4294967295 OCTET ; size OCTETs - </doc> - </type> - - <type name="map" code="0xa8" variable-width="4" label="a mapping of keys to typed values"> - <doc> - A map is a set of distinct keys where each key has an associated (type,value) pair. The triple - of the key, type, and value, form an entry within a map. Each entry within a given map MUST - have a distinct key. A map is encoded as a size in octets, a count of the number of entries, - followed by the encoded entries themselves. - </doc> - - <doc> - An encoded map may contain up to (4294967295 - 4) octets worth of encoded entries. The size is - encoded as a 32-bit unsigned integral value in network byte order equal to the number of - octets worth of encoded entries plus 4. (The extra 4 octets is added for the entry count.) The - size is then followed by the number of entries encoded as a 32-bit unsigned integral value in - network byte order. Finally the entries are encoded sequentially. - </doc> - - <doc> - An entry is encoded as the key, followed by the type, and then the value. The key is always a - string encoded as a str8. The type is a single octet that may contain any valid AMQP type - code. The value is encoded according to the rules defined by the type code for that entry. - </doc> - - <doc type="picture" title="Wire Format"> - +------------= size OCTETs =-----------+ - | | - 4 OCTETs | 4 OCTETs | - +----------+----------+-----+---------------+-----+ - | size | count | .../| entry |\... | - +----------+----------+---/ +---------------+ \---+ - uint32 uint32 / / \ \ - / / \ \ - / / \ \ - / / \ \ - / / \ \ - / k OCTETs 1 OCTET n OCTETs \ - +-----------+---------+-----------+ - | key | type | value | - +-----------+---------+-----------+ - str8 *type* - </doc> - - <doc type="bnf"> - map = size count *entry - - size = uint32 ; size of count and entries in octets - count = uint32 ; number of entries in the map - - entry = key type value - key = str8 - type = OCTET ; type code of the value - value = *OCTET ; the encoded value - </doc> - </type> - - <type name="list" code="0xa9" variable-width="4" label="a series of consecutive type-value pairs"> - <doc> - A list is an ordered sequence of (type, value) pairs. The (type, value) pair forms an item - within the list. The list may contain items of many distinct types. A list is encoded as a - size in octets, followed by a count of the number of items, followed by the items themselves - encoded in their defined order. - </doc> - - <doc> - An encoded list may contain up to (4294967295 - 4) octets worth of encoded items. The size is - encoded as a 32-bit unsigned integral value in network byte order equal to the number of - octets worth of encoded items plus 4. (The extra 4 octets is added for the item count.) The - size is then followed by the number of items encoded as a 32-bit unsigned integral value in - network byte order. Finally the items are encoded sequentially in their defined order. - </doc> - - <doc> - An item is encoded as the type followed by the value. The type is a single octet that may - contain any valid AMQP type code. The value is encoded according to the rules defined by the - type code for that item. - </doc> - - <doc type="picture" title="Wire Format"> - +---------= size OCTETs =---------+ - | | - 4 OCTETs | 4 OCTETs | - +----------+----------+-----+----------+-----+ - | size | count | .../| item |\... | - +----------+----------+---/ +----------+ \---+ - uint32 uint32 / / \ \ - / / \ \ - / 1 OCTET n OCTETs \ - +----------+-----------+ - | type | value | - +----------+-----------+ - *type* - </doc> - - <doc type="bnf"> - list = size count *item - - size = uint32 ; size of count and items in octets - count = uint32 ; number of items in the list - - item = type value - type = OCTET ; type code of the value - value = *OCTET ; the encoded value - </doc> - </type> - - <type name="array" code="0xaa" variable-width="4" - label="a defined length collection of values of a single type"> - <doc> - An array is an ordered sequence of values of the same type. The array is encoded in as a size - in octets, followed by a type code, then a count of the number values in the array, and - finally the values encoded in their defined order. - </doc> - - <doc> - An encoded array may contain up to (4294967295 - 5) octets worth of encoded values. The size - is encoded as a 32-bit unsigned integral value in network byte order equal to the number of - octets worth of encoded values plus 5. (The extra 5 octets consist of 4 octets for the count - of the number of values, and one octet to hold the type code for the items in the array.) The - size is then followed by a single octet that may contain any valid AMQP type code. The type - code is then followed by the number of values encoded as a 32-bit unsigned integral value in - network byte order. Finally the values are encoded sequentially in their defined order - according to the rules defined by the type code for the array. - </doc> - - <doc type="picture" title="Wire Format"> - 4 OCTETs 1 OCTET 4 OCTETs (size - 5) OCTETs - +----------+---------+----------+-------------------------+ - | size | type | count | values | - +----------+---------+----------+-------------------------+ - uint32 uint32 *count* encoded *types* - </doc> - - <doc type="bnf"> - array = size type count values - - size = uint32 ; size of type, count, and values in octets - type = OCTET ; the type of the encoded values - count = uint32 ; number of items in the array - - values = 0*4294967290 OCTET ; (size - 5) OCTETs - </doc> - </type> - - <type name="struct32" code="0xab" variable-width="4" label="a coded struct with a 32-bit size"> - <doc> - The struct32 type describes any coded struct with a 32-bit (4 octet) size. The type is - restricted to be only coded structs with a 32-bit size, consequently the first six octets of - any encoded value for this type MUST always contain the size, class-code, and struct-code in - that order. - </doc> - - <doc> - The size is encoded as a 32-bit unsigned integral value in network byte order that is equal to - the size of the encoded field-data, packing-flags, class-code, and struct-code. The class-code - is a single octet that may be set to any valid class code. The struct-code is a single octet - that may be set to any valid struct code within the given class-code. - </doc> - - <doc> - The first six octets are then followed by the packing flags and encoded field data. The - presence and quantity of packing-flags, as well as the specific fields are determined by the - struct definition identified with the encoded class-code and struct-code. - </doc> - - <doc type="picture" title="Wire Format"> - 4 OCTETs 1 OCTET 1 OCTET pack-width OCTETs n OCTETs - +----------+------------+-------------+-------------------+------------+ - | size | class-code | struct-code | packing-flags | field-data | - +----------+------------+-------------+-------------------+------------+ - uint32 - - n = (size - 2 - pack-width) - </doc> - - <doc type="bnf"> - struct32 = size class-code struct-code packing-flags field-data - - size = uint32 - - class-code = OCTET ; zero for top-level structs - struct-code = OCTET ; together with class-code identifies the struct - ; definition which determines the pack-width and - ; fields - - packing-flags = 0*4 OCTET ; pack-width OCTETs - - field-data = *OCTET ; (size - 2 - pack-width) OCTETs - </doc> - </type> - - <!-- - 0xb0 - 0xbf: Reserved - --> - - <!-- - 0xc0 - 0xcf:Fixed width types - 5 octets - --> - - <type name="bin40" code="0xc0" fixed-width="5" label="five octets of unspecified binary encoding"> - <doc> - The bin40 type consists of five consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-------------+------------+------------+ - | octet-one | octet-two | octet-three | octet-four | octet-five | - +-----------+-----------+-------------+------------+------------+ - </doc> - - <doc type="bnf"> - bin40 = 5 OCTET - </doc> - </type> - - <type name="dec32" code="0xc8" fixed-width="5" - label="32-bit decimal value (e.g. for use in financial values)"> - <doc> - The dec32 type is decimal value with a variable number of digits following the decimal point. - It is encoded as an 8-bit unsigned integral value representing the number of decimal places. - This is followed by the signed integral value encoded using a 32-bit two's complement - representation in network byte order. - </doc> - - <doc> - The former value is referred to as the exponent of the divisor. The latter value is the - mantissa. The decimal value is given by: mantissa / 10^exponent. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 4 OCTETs - +----------+----------+ - | exponent | mantissa | - +----------+----------+ - uint8 int32 - </doc> - - <doc type="bnf"> - dec32 = exponent mantissa - exponent = uint8 - mantissa = int32 - </doc> - </type> - - <!-- - 0xd0 - 0xdf: Fixed width types - 9 octets - --> - - <type name="bin72" code="0xd0" fixed-width="9" - label="nine octets of unspecified binary encoding"> - <doc> - The bin72 type consists of nine consecutive octets of opaque binary data. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 1 OCTET 1 OCTET 1 OCTET - +-----------+-----------+-----+-------------+------------+ - | octet-one | octet-two | ... | octet-eight | octet-nine | - +-----------+-----------+-----+-------------+------------+ - </doc> - - <doc type="bnf"> - bin64 = 9 OCTET - </doc> - </type> - - <type name="dec64" code="0xd8" fixed-width="9" - label="64-bit decimal value (e.g. for use in financial values)"> - <doc> - The dec64 type is decimal value with a variable number of digits following the decimal point. - It is encoded as an 8-bit unsigned integral value representing the number of decimal places. - This is followed by the signed integral value encoded using a 64-bit two's complement - representation in network byte order. - </doc> - - <doc> - The former value is referred to as the exponent of the divisor. The latter value is the - mantissa. The decimal value is given by: mantissa / 10^exponent. - </doc> - - <doc type="picture" title="Wire Format"> - 1 OCTET 8 OCTETs - +----------+----------+ - | exponent | mantissa | - +----------+----------+ - uint8 int64 - </doc> - - <doc type="bnf"> - dec64 = exponent mantissa - exponent = uint8 - mantissa = int64 - </doc> - </type> - - <!-- - 0xe0 - 0xef: Reserved - --> - - <!-- - 0xf0 - 0xff: Zero-length types - --> - - <type name="void" code="0xf0" fixed-width="0" label="the void type"> - <doc> - The void type is used within tagged data structures such as maps and lists to indicate an - empty value. The void type has no value and is encoded as an empty sequence of octets. - </doc> - </type> - - <type name="bit" code="0xf1" fixed-width="0" label="presence indicator"> - <doc> - The bit type is used to indicate that a packing flag within a packed struct is being used to - represent a boolean value based on the presence of an empty value. The bit type has no value - and is encoded as an empty sequence of octets. - </doc> - </type> - - <!-- - ====================================================== - == CONSTANTS - ====================================================== - --> - - <!-- Protocol constants --> - - <constant name="MIN-MAX-FRAME-SIZE" value="4096" label="The minimum size (in bytes) which can be - agreed upon as the maximum frame size."> - <doc> - During the initial connection negotiation, the two peers must agree upon a maximum frame size. - This constant defines the minimum value to which the maximum frame size can be set. By - defining this value, the peers can guarantee that they can send frames of up to this size - until they have agreed a definitive maximum frame size for that connection. - </doc> - </constant> - - <!-- - ====================================================== - == DOMAIN TYPES - ====================================================== - --> - - <!-- Segment types --> - - <domain name="segment-type" type="uint8" label="valid values for the frame type indicator."> - <doc> - Segments are defined in <xref ref="specification.transport.assemblies_segments_and_frames"/>. - The segment domain defines the valid values that may be used for the segment indicator within - the frame header. - </doc> - - <enum> - <choice name="control" value="0"> - <doc> - The frame type indicator for Control segments (see <xref - ref="specification.formal_notation.controls"/>). - </doc> - </choice> - <choice name="command" value="1"> - <doc> - The frame type indicator for Command segments (see <xref - ref="specification.formal_notation.commands"/>). - </doc> - </choice> - <choice name="header" value="2" > - <doc> - The frame type indicator for Header segments (see <xref - ref="specification.formal_notation.segments.header"/>). - </doc> - </choice> - <choice name="body" value="3" > - <doc> - The frame type indicator for Body segments (see <xref - ref="specification.formal_notation.segments.body"/>). - </doc> - </choice> - </enum> - </domain> - - <!-- Tracks --> - - <domain name="track" type="uint8" label="Valid values for transport level tracks"> - <doc> Tracks are defined in <xref ref="specification.transport.channels_and_tracks"/>. The - track domain defines the valid values that may used for the track indicator within the frame - header</doc> - <enum> - <choice name="control" value="0"> - <doc> - The track used for all controls. All controls defined in this specification MUST be sent - on track 0. - </doc> - </choice> - <choice name="command" value="1"> - <doc> - The track used for all commands. All commands defined in this specification MUST be sent - on track 1. - </doc> - </choice> - </enum> - </domain> - - - <domain name="str16-array" type="array" label="An array of values of type str16."> - <doc> - An array of values of type str16. - </doc> - </domain> - - - - <!-- == Class: connection ==================================================================== --> - - <class name="connection" code="0x1" label="work with connections"> - <doc> - The connection class provides controls for a client to establish a network connection to a - server, and for both peers to operate the connection thereafter. - </doc> - - <doc type="grammar"> - connection = open-connection - *use-connection - close-connection - open-connection = C:protocol-header - S:START C:START-OK - *challenge - S:TUNE C:TUNE-OK - C:OPEN S:OPEN-OK | S:REDIRECT - challenge = S:SECURE C:SECURE-OK - use-connection = *channel - close-connection = C:CLOSE S:CLOSE-OK - / S:CLOSE C:CLOSE-OK - </doc> - - <role name="server" implement="MUST" /> - <role name="client" implement="MUST" /> - - <domain name="close-code" type="uint16" label="code used in the connection.close control to - indicate reason for closure"> - <enum> - <choice name="normal" value="200"> - <doc> - The connection closed normally. - </doc> - </choice> - - <choice name="connection-forced" value="320"> - <doc> - An operator intervened to close the connection for some reason. The client may retry at - some later date. - </doc> - </choice> - - <choice name="invalid-path" value="402"> - <doc> - The client tried to work with an unknown virtual host. - </doc> - </choice> - - <choice name="framing-error" value="501"> - <doc> - A valid frame header cannot be formed from the incoming byte stream. - </doc> - </choice> - </enum> - </domain> - - <domain name="amqp-host-url" type="str16" label="URL for identifying an AMQP Server"> - <doc> - The amqp-url domain defines a format for identifying an AMQP Server. It is used to provide - alternate hosts in the case where a client has to reconnect because of failure, or because - the server requests the client to do so upon initial connection. - </doc> - <doc type="bnf"><![CDATA[ - amqp_url = "amqp:" prot_addr_list - prot_addr_list = [prot_addr ","]* prot_addr - prot_addr = tcp_prot_addr | tls_prot_addr - - tcp_prot_addr = tcp_id tcp_addr - tcp_id = "tcp:" | "" - tcp_addr = [host [":" port] ] - host = <as per http://www.ietf.org/rfc/rfc3986.txt> - port = number]]> - </doc> - </domain> - - <domain name="amqp-host-array" type="array" label="An array of values of type amqp-host-url"> - <doc> - Used to provide a list of alternate hosts. - </doc> - </domain> - - <!-- - Control: connection.start - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="start" code="0x1" label="start connection negotiation"> - <doc> - This control starts the connection negotiation process by telling the client the supported - security mechanisms and locales from which the client can choose. - </doc> - - <rule name="protocol-name"> - <doc> - If the server cannot support the protocol specified in the protocol header, it MUST close - the socket connection without sending any response control. - </doc> - <doc type="scenario"> - The client sends a protocol header containing an invalid protocol name. The server must - respond by closing the connection. - </doc> - </rule> - - <rule name="client-support"> - <doc> - If the client cannot handle the protocol version suggested by the server it MUST close the - socket connection. - </doc> - <doc type="scenario"> - The server sends a protocol version that is lower than any valid implementation, e.g. 0.1. - The client must respond by closing the connection. - </doc> - </rule> - - <implement role="client" handle="MUST" /> - - <response name="start-ok" /> - - <field name="server-properties" type="map" label="server properties"> - <rule name="required-fields"> - <doc> - The properties SHOULD contain at least these fields: "host", specifying the server host - name or address, "product", giving the name of the server product, "version", giving the - name of the server version, "platform", giving the name of the operating system, - "copyright", if appropriate, and "information", giving other general information. - </doc> - <doc type="scenario"> - Client connects to server and inspects the server properties. It checks for the presence - of the required fields. - </doc> - </rule> - </field> - - <field name="mechanisms" type="str16-array" label="available security mechanisms" - required="true"> - <doc> - A list of the security mechanisms that the server supports. - </doc> - </field> - - <field name="locales" type="str16-array" label="available message locales" required="true"> - <doc> - A list of the message locales that the server supports. The locale defines the language in - which the server will send reply texts. - </doc> - - <rule name="required-support"> - <doc> - The server MUST support at least the en_US locale. - </doc> - <doc type="scenario"> - Client connects to server and inspects the locales field. It checks for the presence of - the required locale(s). - </doc> - </rule> - </field> - </control> - - <!-- - Control: connection.start-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="start-ok" code="0x2" label="select security mechanism and locale"> - <doc> - This control selects a SASL security mechanism. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="client-properties" type="map" label="client properties"> - <rule name="required-fields"> - <!-- This rule is not testable from the client side --> - <doc> - The properties SHOULD contain at least these fields: "product", giving the name of the - client product, "version", giving the name of the client version, "platform", giving the - name of the operating system, "copyright", if appropriate, and "information", giving - other general information. - </doc> - </rule> - </field> - - <field name="mechanism" type="str8" label="selected security mechanism" required="true"> - <doc> - A single security mechanisms selected by the client, which must be one of those specified - by the server. - </doc> - - <rule name="security"> - <doc> - The client SHOULD authenticate using the highest-level security profile it can handle - from the list provided by the server. - </doc> - </rule> - - <rule name="validity"> - <doc> - If the mechanism field does not contain one of the security mechanisms proposed by the - server in the Start control, the server MUST close the connection without sending any - further data. - </doc> - <doc type="scenario"> - Client connects to server and sends an invalid security mechanism. The server must - respond by closing the connection (a socket close, with no connection close - negotiation). - </doc> - </rule> - </field> - - <field name="response" type="vbin32" label="security response data" required="true"> - <doc> - A block of opaque data passed to the security mechanism. The contents of this data are - defined by the SASL security mechanism. - </doc> - </field> - - <field name="locale" type="str8" label="selected message locale" required="true"> - <doc> - A single message locale selected by the client, which must be one of those specified by - the server. - </doc> - </field> - </control> - - <!-- - Control: connection.secure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="secure" code="0x3" label="security mechanism challenge"> - <doc> - The SASL protocol works by exchanging challenges and responses until both peers have - received sufficient information to authenticate each other. This control challenges the - client to provide more information. - </doc> - - <implement role="client" handle="MUST" /> - - <response name="secure-ok" /> - - <field name="challenge" type="vbin32" label="security challenge data" required="true"> - <doc> - Challenge information, a block of opaque binary data passed to the security mechanism. - </doc> - </field> - </control> - - <!-- - Control: connection.secure-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="secure-ok" code="0x4" label="security mechanism response"> - <doc> - This control attempts to authenticate, passing a block of SASL data for the security - mechanism at the server side. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="response" type="vbin32" label="security response data" required="true"> - <doc> - A block of opaque data passed to the security mechanism. The contents of this data are - defined by the SASL security mechanism. - </doc> - </field> - </control> - - <!-- - Control: connection.tune - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="tune" code="0x5" label="propose connection tuning parameters"> - <doc> - This control proposes a set of connection configuration values to the client. The client can - accept and/or adjust these. - </doc> - - <implement role="client" handle="MUST" /> - - <response name="tune-ok" /> - - <field name="channel-max" type="uint16" label="proposed maximum channels"> - <doc> - The maximum total number of channels that the server allows per connection. If this is not - set it means that the server does not impose a fixed limit, but the number of allowed - channels may be limited by available server resources. - </doc> - </field> - - <field name="max-frame-size" type="uint16" label="proposed maximum frame size"> - <doc> - The largest frame size that the server proposes for the connection. The client can - negotiate a lower value. If this is not set means that the server does not impose any - specific limit but may reject very large frames if it cannot allocate resources for them. - </doc> - - <rule name="minimum"> - <doc> - Until the max-frame-size has been negotiated, both peers MUST accept frames of up to - MIN-MAX-FRAME-SIZE octets large, and the minimum negotiated value for max-frame-size is - also MIN-MAX-FRAME-SIZE. - </doc> - <doc type="scenario"> - Client connects to server and sends a large properties field, creating a frame of - MIN-MAX-FRAME-SIZE octets. The server must accept this frame. - </doc> - </rule> - </field> - - <field name="heartbeat-min" type="uint16" label="the minimum supported heartbeat delay"> - <doc> - The minimum delay, in seconds, of the connection heartbeat supported by the server. If - this is not set it means the server does not support sending heartbeats. - </doc> - </field> - - <field name="heartbeat-max" type="uint16" label="the maximum supported heartbeat delay"> - <doc> - The maximum delay, in seconds, of the connection heartbeat supported by the server. If - this is not set it means the server has no maximum. - </doc> - - <rule name="permitted-range"> - <doc> - The heartbeat-max value must be greater than or equal to the value supplied in the - heartbeat-min field. - </doc> - </rule> - - <rule name="no-heartbeat-min"> - <doc> - If no heartbeat-min is supplied, then the heartbeat-max field MUST remain empty. - </doc> - </rule> - </field> - </control> - - <!-- - Control: connection.tune-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="tune-ok" code="0x6" label="negotiate connection tuning parameters"> - <doc> - This control sends the client's connection tuning parameters to the server. Certain fields - are negotiated, others provide capability information. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="channel-max" type="uint16" label="negotiated maximum channels" required="true"> - <doc> - The maximum total number of channels that the client will use per connection. - </doc> - - <rule name="upper-limit"> - <doc> - If the client specifies a channel max that is higher than the value provided by the - server, the server MUST close the connection without attempting a negotiated close. The - server may report the error in some fashion to assist implementers. - </doc> - - </rule> - - <rule name="available-channels"> - <doc> - If the client agrees to a channel-max of N channels, then the channels available for - communication between client and server are precisely the channels numbered 0 to (N-1). - </doc> - </rule> - </field> - - <field name="max-frame-size" type="uint16" label="negotiated maximum frame size"> - <doc> - The largest frame size that the client and server will use for the connection. If it is - not set means that the client does not impose any specific limit but may reject very large - frames if it cannot allocate resources for them. Note that the max-frame-size limit - applies principally to content frames, where large contents can be broken into frames of - arbitrary size. - </doc> - - <rule name="minimum"> - <doc> - Until the max-frame-size has been negotiated, both peers MUST accept frames of up to - MIN-MAX-FRAME-SIZE octets large, and the minimum negotiated value for max-frame-size is - also MIN-MAX-FRAME-SIZE. - </doc> - </rule> - - <rule name="upper-limit"> - <doc> - If the client specifies a max-frame-size that is higher than the value provided by the - server, the server MUST close the connection without attempting a negotiated close. The - server may report the error in some fashion to assist implementers. - </doc> - </rule> - - <rule name="max-frame-size"> - <doc> - A peer MUST NOT send frames larger than the agreed-upon size. A peer that receives an - oversized frame MUST close the connection with the framing-error close-code. - </doc> - </rule> - </field> - - <field name="heartbeat" type="uint16" label="negotiated heartbeat delay"> - <doc> - The delay, in seconds, of the connection heartbeat chosen by the client. If it is not set - it means the client does not want a heartbeat. - </doc> - - <rule name="permitted-range"> - <doc> - The chosen heartbeat MUST be in the range supplied by the heartbeat-min and - heartbeat-max fields of connection.tune. - </doc> - </rule> - - <rule name="no-heartbeat-min"> - <doc> - The heartbeat field MUST NOT be set if the heartbeat-min field of connection.tune was - not set by the server. - </doc> - </rule> - </field> - </control> - - <!-- - Control: connection.open - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="open" code="0x7" label="open connection to virtual host"> - <doc> - This control opens a connection to a virtual host, which is a collection of resources, and - acts to separate multiple application domains within a server. The server may apply - arbitrary limits per virtual host, such as the number of each type of entity that may be - used, per connection and/or in total. - </doc> - - <implement role="server" handle="MUST" /> - - <response name="open-ok" /> - <response name="redirect" /> - - <field name="virtual-host" type="str8" label="virtual host name" required="true"> - <doc> - The name of the virtual host to work with. - </doc> - - <rule name="separation"> - <doc> - If the server supports multiple virtual hosts, it MUST enforce a full separation of - exchanges, queues, and all associated entities per virtual host. An application, - connected to a specific virtual host, MUST NOT be able to access resources of another - virtual host. - </doc> - </rule> - - <rule name="security"> - <doc> - The server SHOULD verify that the client has permission to access the specified virtual - host. - </doc> - </rule> - </field> - - <field name="capabilities" type="str16-array" label="required capabilities"> - <doc> - The client can specify zero or more capability names. The server can use this to determine - how to process the client's connection request. - </doc> - </field> - - <field name="insist" type="bit" label="insist on connecting to server"> - <doc> - In a configuration with multiple collaborating servers, the server may respond to a - connection.open control with a Connection.Redirect. The insist option tells the server - that the client is insisting on a connection to the specified server. - </doc> - <rule name="behavior"> - <doc> - When the client uses the insist option, the server MUST NOT respond with a - Connection.Redirect control. If it cannot accept the client's connection request it - should respond by closing the connection with a suitable reply code. - </doc> - </rule> - </field> - </control> - - <!-- - Control: connection.open-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="open-ok" code="0x8" label="signal that connection is ready"> - <doc> - This control signals to the client that the connection is ready for use. - </doc> - - <implement role="client" handle="MUST" /> - - <field name="known-hosts" type="amqp-host-array" label="alternate hosts which may be used in - the case of failure"> - <doc> - Specifies an array of equivalent or alternative hosts that the server knows about, which - will normally include the current server itself. Each entry in the array will be in the - form of an IP address or DNS name, optionally followed by a colon and a port number. - Clients can cache this information and use it when reconnecting to a server after a - failure. This field may be empty. - </doc> - </field> - </control> - - <!-- - Control: connection.redirect - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="redirect" code="0x9" label="redirects client to other server"> - <doc> - This control redirects the client to another server, based on the requested virtual host - and/or capabilities. - </doc> - - <rule name="usage"> - <doc> - When getting the connection.redirect control, the client SHOULD reconnect to the host - specified, and if that host is not present, to any of the hosts specified in the - known-hosts list. - </doc> - </rule> - - <implement role="client" handle="MUST" /> - - <field name="host" type="amqp-host-url" label="server to connect to" required="true"> - <doc> - Specifies the server to connect to. - </doc> - </field> - - <field name="known-hosts" type="amqp-host-array" label="alternate hosts to try in case of - failure"> - <doc> - An array of equivalent or alternative hosts that the server knows about. - </doc> - </field> - </control> - - <!-- - Control: connection.heartbeat - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="heartbeat" code="0xa" label="indicates connection is still alive"> - <doc> - The heartbeat control may be used to generate artificial network traffic when a connection - is idle. If a connection is idle for more than twice the negotiated heartbeat delay, the - peers MAY be considered disconnected. - </doc> - <implement role="client" handle="MAY" /> - <implement role="server" handle="MAY" /> - </control> - - <!-- - Control: connection.close - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="close" code="0xb" label="request a connection close"> - <doc> - This control indicates that the sender wants to close the connection. The reason for close - is indicated with the reply-code and reply-text. The channel this control is sent on MAY be - used to indicate which channel caused the connection to close. - </doc> - - <implement role="client" handle="MUST" /> - <implement role="server" handle="MUST" /> - - <response name="close-ok" /> - - <field name="reply-code" type="close-code" label="the numeric reply code" - required="true"> - <doc> - Indicates the reason for connection closure. - </doc> - </field> - <field name="reply-text" type="str8" label="the localized reply text"> - <doc> - This text can be logged as an aid to resolving issues. - </doc> - </field> - </control> - - <!-- - Control: connection.close-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <control name="close-ok" code="0xc" label="confirm a connection close"> - <doc> - This control confirms a connection.close control and tells the recipient that it is safe to - release resources for the connection and close the socket. - </doc> - - <rule name="reporting"> - <doc> - A peer that detects a socket closure without having received a Close-Ok handshake control - SHOULD log the error. - </doc> - </rule> - - <implement role="client" handle="MUST" /> - <implement role="server" handle="MUST" /> - </control> - - </class> - - <!-- == Class: session ======================================================================= --> - - <class name="session" code="0x2" label="session controls"> - <doc> - A session is a named interaction between two peers. Session names are chosen by the upper - layers and may be used indefinitely. The model layer may associate long-lived or durable state - with a given session name. The session layer provides transport of commands associated with - this interaction. - </doc> - - <doc> - The controls defined within this class are specified in terms of the "sender" of commands and - the "receiver" of commands. Since both client and server send and receive commands, the - overall session dialog is symmetric, however the semantics of the session controls are defined - in terms of a single sender/receiver pair, and it is assumed that the client and server will - each contain both a sender and receiver implementation. - </doc> - - <rule name="attachment"> - <doc> - The transport MUST be attached in order to use any control other than "attach", "attached", - "detach", or "detached". A peer receiving any other control on a detached transport MUST - discard it and send a session.detached with the "not-attached" reason code. - </doc> - </rule> - - <role name="server" implement="MUST" /> - <role name="client" implement="MUST" /> - - <role name="sender" implement="MUST"> - <doc> - The sender of commands. - </doc> - </role> - <role name="receiver" implement="MUST"> - <doc> - The receiver of commands. - </doc> - </role> - - <domain name="name" type="vbin16" label="opaque session name"> - <doc> - The session name uniquely identifies an interaction between two peers. It is scoped to a - given authentication principal. - </doc> - </domain> - - <domain name="detach-code" type="uint8" label="reason for detach"> - <enum> - <choice name="normal" value="0"> - <doc> - The session was detached by request. - </doc> - </choice> - <choice name="session-busy" value="1"> - <doc> - The session is currently attached to another transport. - </doc> - </choice> - <choice name="transport-busy" value="2"> - <doc> - The transport is currently attached to another session. - </doc> - </choice> - <choice name="not-attached" value="3"> - <doc> - The transport is not currently attached to any session. - </doc> - </choice> - <choice name="unknown-ids" value="4"> - <doc> - Command data was received prior to any use of the command-point control. - </doc> - </choice> - </enum> - </domain> - - <domain name="commands" type="sequence-set" label="identifies a set of commands"> - </domain> - - <struct name="header" size="1" pack="1"> - <doc> - The session header appears on commands after the class and command id, but prior to command - arguments. - </doc> - - <field name="sync" type="bit" label="request notification of completion"> - <doc> - Request notification of completion for this command. - </doc> - </field> - </struct> - - <struct name="command-fragment" size="0" pack="0" label="byte-ranges within a set of commands"> - - <field name="command-id" type="sequence-no" required="true"> - - </field> - <field name="byte-ranges" type="byte-ranges" required="true"> - - </field> - </struct> - - <domain name="command-fragments" type="array" label="an array of values of type - command-fragment"/> - - <control name="attach" code="0x1" label="attach to the named session"> - <doc> - Requests that the current transport be attached to the named session. Success or failure - will be indicated with an attached or detached response. This control is idempotent. - </doc> - - <rule name="one-transport-per-session"> - <doc> - A session MUST NOT be attached to more than one transport at a time. - </doc> - </rule> - - <rule name="one-session-per-transport"> - <doc> - A transport MUST NOT be attached to more than one session at a time. - </doc> - </rule> - - <rule name="idempotence"> - <doc> - Attaching a session to its current transport MUST succeed and result in an attached - response. - </doc> - </rule> - - <rule name="scoping"> - <doc> - Attachment to the same session name from distinct authentication principals MUST succeed. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MAY" /> - - <response name="attached"/> - <response name="detached"/> - - <field name="name" type="name" label="the session name" required="true"> - <doc> - Identifies the session to be attached to the current transport. - </doc> - </field> - - <field name="force" type="bit" label="force attachment to a busy session"> - <doc> - If set then a busy session will be forcibly detached from its other transport and - reattached to the current transport. - </doc> - </field> - </control> - - <control name="attached" code="0x2" label="confirm attachment to the named session"> - <doc> - Confirms successful attachment of the transport to the named session. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="name" type="name" label="the session name" required="true"> - <doc> - Identifies the session now attached to the current transport. - </doc> - </field> - </control> - - <control name="detach" code="0x3" label="detach from the named session"> - <doc> - Detaches the current transport from the named session. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <response name="detached"/> - - <field name="name" type="name" label="the session name" required="true"> - <doc> - Identifies the session to detach. - </doc> - </field> - </control> - - <control name="detached" code="0x4" label="confirm detachment from the named session"> - <doc> - Confirms detachment of the current transport from the named session. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="name" type="name" label="the session name" required="true"> - <doc> - Identifies the detached session. - </doc> - </field> - <field name="code" type="detach-code" label="the reason for detach" required="true"> - <doc> - Identifies the reason for detaching from the named session. - </doc> - </field> - </control> - - <!-- - Execution state is the set of confirmed, and completed incoming commands, as well as the set - of outgoing in-doubt commands held for replay. - --> - - <control name="request-timeout" code="0x5" label="requests the execution timeout be changed"> - <doc> - This control may be sent by either the sender or receiver of commands. It requests that the - execution timeout be changed. This is the minimum amount of time that a peer must preserve - execution state for a detached session. - </doc> - - <rule name="maximum-granted-timeout"> - <doc> - The handler of this request MUST set his timeout to the maximum allowed value less than or - equal to the requested timeout, and MUST convey the chosen timeout in the response. - </doc> - </rule> - - <implement role="sender" handle="MUST" /> - <implement role="receiver" handle="MUST" /> - - <response name="timeout"/> - - <field name="timeout" type="uint32" label="the requested timeout"> - <doc> - The requested timeout for execution state in seconds. If not set, this control requests - that execution state is preserved indefinitely. - </doc> - </field> - </control> - - <control name="timeout" code="0x6" label="the granted timeout"> - <doc> - This control may be sent by the either the sender or receiver of commands. It is a - one-to-one reply to the request-timeout control that indicates the granted timeout for - execution state. - </doc> - - <implement role="sender" handle="MUST" /> - <implement role="receiver" handle="MUST" /> - - <field name="timeout" type="uint32" label="the execution timeout"> - <doc> - The timeout for execution state. If not set, then execution state is preserved - indefinitely. - </doc> - </field> - </control> - - <control name="command-point" code="0x7" - label="the command id and byte offset of subsequent data"> - <doc> - This control is sent by the sender of commands and handled by the receiver of commands. This - establishes the sequence numbers associated with all subsequent command data sent from the - sender to the receiver. The subsequent command data will be numbered starting with the - values supplied in this control and proceeding sequentially. This must be used at least once - prior to sending any command data on newly attached transports. - </doc> - - <rule name="newly-attached-transports"> - <doc> - If command data is sent on a newly attached transport the session MUST be detached with an - "unknown-id" reason-code. - </doc> - </rule> - - <rule name="zero-offset"> - <doc> - If the offset is zero, the next data frame MUST have the first-frame and first-segment - flags set. Violation of this is a framing error. - </doc> - </rule> - - <rule name="nonzero-offset"> - <doc> - If the offset is nonzero, the next data frame MUST NOT have both the first-frame and - first-segment flag set. Violation of this is a framing error. - </doc> - </rule> - - <implement role="receiver" handle="MUST" /> - - <field name="command-id" type="sequence-no" label="the command-id of the next command" - required="true"/> - <field name="command-offset" type="uint64" label="the byte offset within the command" - required="true"/> - </control> - - <control name="expected" code="0x8" label="informs the peer of expected commands"> - <doc> - This control is sent by the receiver of commands and handled by the sender of commands. It - informs the sender of what commands and command fragments are expected at the receiver. - This control is only sent in response to a flush control with the expected flag set. The - expected control is never sent spontaneously. - </doc> - - <rule name="include-next-command"> - <doc> - The set of expected commands MUST include the next command after the highest seen command. - </doc> - </rule> - - <rule name="commands-empty-means-new-session"> - <doc> - The set of expected commands MUST have zero elements if and only if the sender holds no - execution state for the session (i.e. it is a new session). - </doc> - </rule> - - <rule name="no-overlaps"> - <doc> - If a command-id appears in the commands field, it MUST NOT appear in the fragments field. - </doc> - </rule> - - <rule name="minimal-fragments"> - <doc> - When choice is permitted, a command MUST appear in the commands field rather than the - fragments field. - </doc> - </rule> - - <implement role="sender" handle="MUST" /> - - <field name="commands" type="commands" label="expected commands" required="true"/> - <field name="fragments" type="command-fragments" label="expected fragments" /> - </control> - - <control name="confirmed" code="0x9" label="notifies of confirmed commands"> - <doc> - This control is sent by the receiver of commands and handled by the sender of commands. This - sends the set of commands that will definitely be completed by this peer to the sender. This - excludes commands known by the receiver to be considered confirmed or complete at the - sender. - </doc> - <doc> - This control must be sent if the partner requests the set of confirmed commands using the - session.flush control with the confirmed flag set. - </doc> - <doc> - This control may be sent spontaneously. One reason for separating confirmation from - completion is for large persistent messages, where the receipt (and storage to a durable - store) of part of the message will result in less data needing to be replayed in the case of - transport failure during transmission. - </doc> - <doc> - A simple implementation of an AMQP client or server may be implemented to take no action on - receipt of session.confirmed controls, and take action only when receiving - session.completed controls. - </doc> - <doc> - A simple implementation of an AMQP client or server may be implemented such that it never - spontaneously sends session.confirmed and that when requested for the set of confirmed - commands (via the session.flush control) it responds with the same set of commands as it - would to when the set of completed commands was requested (trivially all completed commands - are confirmed). - </doc> - - <rule name="durability"> - <doc> - If a command has durable implications, it MUST NOT be confirmed until the fact of the - command has been recorded on durable media. - </doc> - </rule> - - <rule name="no-overlaps"> - <doc> - If a command-id appears in the commands field, it MUST NOT appear in the fragments field. - </doc> - </rule> - - <rule name="minimal-fragments"> - <doc> - When choice is permitted, a command MUST appear in the commands field rather than the - fragments field. - </doc> - </rule> - - <implement role="sender" handle="MUST" /> - - <field name="commands" type="commands" label="entirely confirmed commands"> - <rule name="exclude-known-complete"> - <doc> - Command-ids included in prior known-complete replies MUST be excluded from the set of - all confirmed commands. - </doc> - </rule> - </field> - <field name="fragments" type="command-fragments" label="partially confirmed commands"/> - </control> - - <control name="completed" code="0xa" label="notifies of command completion"> - <doc> - This control is sent by the receiver of commands, and handled by the sender of commands. It - informs the sender of all commands completed by the receiver. This excludes commands known - by the receiver to be considered complete at the sender. - </doc> - - <rule name="known-completed-reply"> - <doc> - The sender MUST eventually reply with a known-completed set that covers the completed ids. - </doc> - </rule> - - <rule name="delayed-reply"> - <doc> - The known-complete reply MAY be delayed at the senders discretion if the timely-reply - field is not set. - </doc> - </rule> - - <rule name="merged-reply"> - <doc> - Multiple replies may be merged by sending a single known-completed that includes the union - of the merged command-id sets. - </doc> - </rule> - - <implement role="sender" handle="MUST" /> - - <field name="commands" type="commands" label="completed commands"> - <doc> - The ids of all completed commands. This excludes commands known by the receiver to be - considered complete at the sender. - </doc> - - <rule name="completed-implies-confirmed"> - <doc> - The sender MUST consider any completed commands to also be confirmed. - </doc> - </rule> - - <rule name="exclude-known-complete"> - <doc> - Command-ids included in prior known-complete replies MUST be excluded from the set of - all completed commands. - </doc> - </rule> - </field> - <field name="timely-reply" type="bit"> - <doc> - If set, the sender is no longer free to delay the known-completed reply. - </doc> - </field> - </control> - - <control name="known-completed" code="0xb" label="Inform peer of which commands are known to be - completed"> - <doc> - This control is sent by the sender of commands, and handled by the receiver of commands. It - is sent in reply to one or more completed controls from the receiver. It informs the - receiver that commands are known to be completed by the sender. - </doc> - - <rule name="stateless"> - <doc> - The sender need not keep state to generate this reply. It is sufficient to reply to any - completed control with an exact echo of the completed ids. - </doc> - </rule> - - <implement role="receiver" handle="MUST" /> - - <field name="commands" type="commands" label="commands known to be complete"> - <doc> - The set of completed commands for one or more session.completed controls. - </doc> - - <rule name="known-completed-implies-known-confirmed"> - <doc> - The receiver MUST treat any of the specified commands to be considered by the sender as - confirmed as well as completed. - </doc> - </rule> - </field> - </control> - - <control name="flush" code="0xc" label="requests a session.completed"> - <doc> - This control is sent by the sender of commands and handled by the receiver of commands. It - requests that the receiver produce the indicated command sets. The receiver should issue the - indicated sets at the earliest possible opportunity. - </doc> - - <implement role="receiver" handle="MUST" /> - - <field name="expected" type="bit" label="request notification of expected commands"/> - <field name="confirmed" type="bit" label="request notification of confirmed commands"/> - <field name="completed" type="bit" label="request notification of completed commands"/> - </control> - - <control name="gap" code="0xd" label="indicates missing segments in the stream"> - <doc> - This control is sent by the sender of commands and handled by the receiver of commands. It - sends command ranges for which there will be no further data forthcoming. The receiver - should proceed with the next available commands that arrive after the gap. - </doc> - - <rule name="gap-confirmation-and-completion"> - <doc> - The command-ids covered by a session.gap MUST be added to the completed and confirmed sets - by the receiver. - </doc> - </rule> - - <rule name="aborted-commands"> - <doc> - If a session.gap covers a partially received command, the receiving peer MUST treat the - command as aborted. - </doc> - </rule> - - <rule name="completed-or-confirmed-commands"> - <doc> - If a session.gap covers a completed or confirmed command, the receiving peer MUST continue - to treat the command as completed or confirmed. - </doc> - </rule> - - <implement role="receiver" handle="MUST" /> - - <field name="commands" type="commands"> - <doc> - The set of command-ids that are contained in this gap. - </doc> - </field> - </control> - - </class> - - <!-- == Class: execution ===================================================================== --> - - <class name="execution" code="0x3" label="execution commands"> - <doc> - The execution class provides commands that carry execution information about other model level - commands. - </doc> - - <role name="server" implement="MUST"/> - <role name="client" implement="MUST"/> - - <domain name="error-code" type="uint16"> - <enum> - <choice name="unauthorized-access" value="403"> - <doc> - The client attempted to work with a server entity to which it has no access due to - security settings. - </doc> - </choice> - - <choice name="not-found" value="404"> - <doc> - The client attempted to work with a server entity that does not exist. - </doc> - </choice> - - <choice name="resource-locked" value="405"> - <doc> - The client attempted to work with a server entity to which it has no access because - another client is working with it. - </doc> - </choice> - - <choice name="precondition-failed" value="406"> - <doc> - The client requested a command that was not allowed because some precondition failed. - </doc> - </choice> - - <choice name="resource-deleted" value="408"> - <doc> - A server entity the client is working with has been deleted. - </doc> - </choice> - - <choice name="illegal-state" value="409"> - <doc> - The peer sent a command that is not permitted in the current state of the session. - </doc> - </choice> - - <choice name="command-invalid" value="503"> - <doc> - The command segments could not be decoded. - </doc> - </choice> - - <choice name="resource-limit-exceeded" value="506"> - <doc> - The client exceeded its resource allocation. - </doc> - </choice> - - <choice name="not-allowed" value="530"> - <doc> - The peer tried to use a command a manner that is inconsistent with the rules described - in the specification. - </doc> - </choice> - - <choice name="illegal-argument" value="531"> - <doc> - The command argument is malformed, i.e. it does not fall within the specified domain. - The illegal-argument exception can be raised on execution of any command which has - domain valued fields. - </doc> - </choice> - - <choice name="not-implemented" value="540"> - <doc> - The peer tried to use functionality that is not implemented in its partner. - </doc> - </choice> - - <choice name="internal-error" value="541"> - <doc> - The peer could not complete the command because of an internal error. The peer may - require intervention by an operator in order to resume normal operations. - </doc> - </choice> - - <choice name="invalid-argument" value="542"> - <doc> - An invalid argument was passed to a command, and the operation could not - proceed. An invalid argument is not illegal (see illegal-argument), i.e. it matches - the domain definition; however the particular value is invalid in this context. - </doc> - </choice> - </enum> - </domain> - - <!-- - Command: execution.sync - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="sync" code="0x1" label="request notification of completion for issued commands"> - <doc> - This command is complete when all prior commands are completed. - </doc> - - <implement role="server" handle="MUST"/> - <implement role="client" handle="MUST"/> - </command> - - <!-- - Command: execution.result - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="result" code="0x2" label="carries execution results"> - <doc> - This command carries data resulting from the execution of a command. - </doc> - - <implement role="server" handle="MUST"/> - <implement role="client" handle="MUST"/> - - <field name="command-id" type="sequence-no" required="true"/> - <field name="value" type="struct32"/> - </command> - - <!-- - Command: execution.exception - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="exception" code="0x3" label="notifies a peer of an execution error"> - <doc> - This command informs a peer of an execution exception. The command-id, when given, - correlates the error to a specific command. - </doc> - - <implement role="client" handle="MUST"/> - <implement role="server" handle="MUST"/> - - <field name="error-code" type="error-code" required="true" label="error code indicating the - type of error"/> - <field name="command-id" type="sequence-no" label="exceptional command"> - <doc> - The command-id of the command which caused the exception. If the exception was not caused - by a specific command, this value is not set. - </doc> - </field> - <field name="class-code" type="uint8" label="the class code of the command whose execution - gave rise to the error (if appropriate)"/> - <field name="command-code" type="uint8" label="the class code of the command whose execution - gave rise to the error (if appropriate)"/> - <field name="field-index" type="uint8" label="index of the exceptional field"> - <doc> - The zero based index of the exceptional field within the arguments to the exceptional - command. If the exception was not caused by a specific field, this value is not set. - </doc> - </field> - <field name="description" type="str16" label="descriptive text on the exception"> - <doc> - The description provided is implementation defined, but MUST be in the language - appropriate for the selected locale. The intention is that this description is suitable - for logging or alerting output. - </doc> - </field> - <field name="error-info" type="map" label="map to carry additional information about the - error"/> - - </command> - - </class> - - <!-- == Class: message ======================================================================= --> - - <class name="message" code="0x4" label="message transfer"> - <doc> - The message class provides commands that support an industry-standard messaging model. - </doc> - - <doc type="picture" title="Transfer States"> - START: - - The message has yet to be sent to the recipient. - - NOT-ACQUIRED: - - The message has been sent to the recipient, but is not - acquired by the recipient. - - ACQUIRED: - - The message has been sent to and acquired by the recipient. - - END: - - The transfer is complete. - </doc> - - <doc type="picture" title="State Transitions"><![CDATA[ - *:TRANSFER (accept-mode=none) *:TRANSFER (acquire-mode=pre-acquired) - +---------------------------------START------------------------------------------+ - | | | - | | *:TRANSFER (acquire-mode=not-acquired) | - | | | - | R:RELEASE \|/ | - | +-------------NOT-ACQUIRED<--+ | - | | | | | R:ACQUIRE (if unavailable) | - | | | +-----+ | - | | | | - | | | R:ACQUIRE (if available) | - | | | | - | | \|/ | - | | ACQUIRED<-------------------------------------------+ - | | | - | | | R:ACCEPT / R:REJECT / R:RELEASE - | | | - | | \|/ - | +------------->END]]> - | /|\ - | | - +-------------------------------+ - </doc> - - <doc type="grammar"> - message = *:TRANSFER [ R:ACQUIRE ] [ R:ACCEPT / R:REJECT / R:RELEASE ] - / *:RESUME - / *:SET-FLOW-MODE - / *:FLOW - / *:STOP - / C:SUBSCRIBE - / C:CANCEL - / C:FLUSH - </doc> - - <rule name="persistent-message"> - <doc> - The server SHOULD respect the delivery-mode property of messages and SHOULD make a - best-effort to hold persistent messages on a reliable storage mechanism. - </doc> - <doc type="scenario"> - Send a persistent message to queue, stop server, restart server and then verify whether - message is still present. Assumes that queues are durable. Persistence without durable - queues makes no sense. - </doc> - </rule> - - <rule name="no-persistent-message-discard"> - <doc> - The server MUST NOT discard a persistent message in case of a queue overflow. - </doc> - <doc type="scenario"> - Create a queue overflow situation with persistent messages and verify that messages do not - get lost (presumably the server will write them to disk). - </doc> - </rule> - - <rule name="throttling"> - <doc> - The server MAY use the message.flow command to slow or stop a message publisher when - necessary. - </doc> - </rule> - - <rule name="non-persistent-message-overflow"> - <doc> - The server MAY overflow non-persistent messages to persistent storage. - </doc> - </rule> - - <rule name="non-persistent-message-discard"> - <doc> - The server MAY discard or dead-letter non-persistent messages on a priority basis if the - queue size exceeds some configured limit. - </doc> - </rule> - - <rule name="min-priority-levels"> - <doc> - The server MUST implement at least 2 priority levels for messages, where priorities 0 and - 9 are treated as two distinct levels. - </doc> - </rule> - - <rule name="priority-level-implementation"> - <doc> - The server SHOULD implement distinct priority levels in the following manner: - </doc> - <doc> - If the server implements n distinct priorities then priorities 0 to 5 - ceiling(n/2) should - be treated equivalently and should be the lowest effective priority. The priorities 4 + - floor(n/2) should be treated equivalently and should be the highest effective priority. The - priorities (5 - ceiling(n/2)) to (4 + floor(n/2)) inclusive must be treated as distinct - priorities. - </doc> - <doc> - Thus, for example, if 2 distinct priorities are implemented, then levels 0 to 4 are - equivalent, and levels 5 to 9 are equivalent and levels 4 and 5 are distinct. If 3 distinct - priorities are implements the 0 to 3 are equivalent, 5 to 9 are equivalent and 3, 4 and 5 - are distinct. - </doc> - <doc> - This scheme ensures that if two priorities are distinct for a server which implements m - separate priority levels they are also distinct for a server which implements n different - priority levels where n > m. - </doc> - </rule> - - <rule name="priority-delivery"> - <doc> - The server MUST deliver messages of the same priority in order irrespective of their - individual persistence. - </doc> - <doc type="scenario"> - Send a set of messages with the same priority but different persistence settings to a queue. - Subscribe and verify that messages arrive in same order as originally published. - </doc> - </rule> - - <role name="server" implement="MUST" /> - <role name="client" implement="MUST" /> - - <domain name="destination" type="str8" label="destination for a message"> - <doc> - Specifies the destination to which the message is to be transferred. - </doc> - </domain> - - <domain name="accept-mode" type="uint8" label="indicates a confirmation mode"> - <doc> - Controls how the sender of messages is notified of successful transfer. - </doc> - - <enum> - <choice name="explicit" value="0"> - <doc> - Successful transfer is signaled by message.accept. An acquired message (whether - acquisition was implicit as in pre-acquired mode or explicit as in not-acquired mode) is - not considered transferred until a message.accept that includes the transfer command is - received. - </doc> - </choice> - - <choice name="none" value="1"> - <doc> - Successful transfer is assumed when accept-mode is "pre-acquired". Messages transferred - with an accept-mode of "not-acquired" cannot be acquired when accept-mode is "none". - </doc> - </choice> - </enum> - </domain> - - <domain name="acquire-mode" type="uint8" label="indicates the transfer mode"> - <doc> - Indicates whether a transferred message can be considered as automatically acquired or - whether an explicit request is necessary in order to acquire it. - </doc> - - <enum> - <choice name="pre-acquired" value="0"> - <doc> - the message is acquired when the transfer starts - </doc> - </choice> - - <choice name="not-acquired" value="1"> - <doc> - the message is not acquired when it arrives, and must be explicitly acquired by the - recipient - </doc> - </choice> - </enum> - </domain> - - <domain name="reject-code" type="uint16" label="reject code for transfer"> - <doc> - Code specifying the reason for a message reject. - </doc> - <enum> - <choice name="unspecified" value="0"> - <doc> - Rejected for an unspecified reason. - </doc> - </choice> - <choice name="unroutable" value="1"> - <doc> - Delivery was attempted but there were no queues which the message could be routed to. - </doc> - </choice> - <choice name="immediate" value="2"> - <doc> - The rejected message had the immediate flag set to true, but at the time of the transfer - at least one of the queues to which it was to be routed did not have any subscriber able - to take the message. - </doc> - </choice> - </enum> - </domain> - - <domain name="resume-id" type="str16"> - <doc> - A resume-id serves to identify partially transferred message content. The id is chosen by - the sender, and must be unique to a given user. A resume-id is not expected to be unique - across users. - </doc> - </domain> - - <domain name="delivery-mode" type="uint8" - label="indicates whether a message should be treated as transient or durable"> - <doc> - - Used to set the reliability requirements for a message which is transferred to the server. - </doc> - <enum> - <choice name="non-persistent" value="1"> - <doc> - A non-persistent message may be lost in event of a failure, but the nature of the - communication is such that an occasional message loss is tolerable. This is the lowest - overhead mode. Non-persistent messages are delivered at most once only. - </doc> - </choice> - - <choice name="persistent" value="2"> - <doc> - A persistent message is one which must be stored on a persistent medium (usually hard - drive) at every stage of delivery so that it will not be lost in event of failure (other - than of the medium itself). This is normally accomplished with some additional overhead. - A persistent message may be delivered more than once if there is uncertainty about the - state of its delivery after a failure and recovery. - </doc> - </choice> - </enum> - </domain> - - <domain name="delivery-priority" type="uint8" - label="indicates the desired priority to assign to a message transfer"> - <doc> - Used to assign a priority to a message transfer. Priorities range from 0 (lowest) to 9 - (highest). - </doc> - <enum> - <choice name="lowest" value="0"> - <doc> - Lowest possible priority message. - </doc> - </choice> - - <choice name="lower" value="1"> - <doc> - Very low priority message - </doc> - </choice> - - <choice name="low" value="2"> - <doc> - Low priority message. - </doc> - </choice> - - <choice name="below-average" value="3"> - <doc> - Below average priority message. - </doc> - </choice> - - <choice name="medium" value="4"> - <doc> - Medium priority message. - </doc> - </choice> - - - <choice name="above-average" value="5"> - <doc> - Above average priority message - </doc> - </choice> - - - <choice name="high" value="6"> - <doc> - High priority message - </doc> - </choice> - - <choice name="higher" value="7"> - <doc> - Higher priority message - </doc> - </choice> - - <choice name="very-high" value="8"> - <doc> - Very high priority message. - </doc> - </choice> - - <choice name="highest" value="9"> - <doc> - Highest possible priority message. - </doc> - </choice> - </enum> - </domain> - - <struct name="delivery-properties" size="4" code="0x1" pack="2"> - <field name="discard-unroutable" type="bit" label="controls discard of unroutable messages"> - <doc> - If set on a message that is not routable the broker can discard it. If not set, an - unroutable message should be handled by reject when accept-mode is explicit; or by routing - to the alternate-exchange if defined when accept-mode is none. - </doc> - </field> - - <field name="immediate" type="bit" label="Consider message unroutable if it cannot be - processed immediately"> - <doc> - If the immediate flag is set to true on a message transferred to a Server, then the - message should be considered unroutable (and not delivered to any queues) if, for any - queue that it is to be routed to according to the standard routing behavior, there is not - a subscription on that queue able to receive the message. The treatment of unroutable - messages is dependent on the value of the discard-unroutable flag. - </doc> - <doc> - The immediate flag is ignored on transferred to a Client. - </doc> - </field> - - <field name="redelivered" type="bit" label="redelivery flag"> - <doc> - This boolean flag indicates that the message may have been previously delivered to this - or another client. - </doc> - <doc> - If the redelivered flag is set on transfer to a Server, then any delivery of the message - from that Server to a Client must also have the redelivered flag set to true. - </doc> - <rule name="implementation"> - <doc> - The server MUST try to signal redelivered messages when it can. When redelivering a - message that was not successfully accepted, the server SHOULD deliver it to the original - client if possible. - </doc> - <doc type="scenario"> - Create a shared queue and publish a message to the queue. Subscribe using explicit - accept-mode, but do not accept the message. Close the session, reconnect, and subscribe - to the queue again. The message MUST arrive with the redelivered flag set. - </doc> - </rule> - <rule name="hinting"> - <doc> - The client should not rely on the redelivered field to detect duplicate messages where - publishers may themselves produce duplicates. A fully robust client should be able to - track duplicate received messages on non-transacted, and locally-transacted sessions. - </doc> - </rule> - </field> - - <field name="priority" type="delivery-priority" label="message priority, 0 to 9" - required="true"> - <doc> Message priority, which can be between 0 and 9. Messages with higher priorities may be - delivered before those with lower priorities. </doc> - </field> - - <field name="delivery-mode" type="delivery-mode" label="message persistence requirement" - required="true"> - <doc> The delivery mode may be non-persistent or persistent. </doc> - </field> - - <field name="ttl" type="uint64" label="time to live in ms"> - <doc> Duration in milliseconds for which the message should be considered "live". If this is - set then a message expiration time will be computed based on the current time plus this - value. Messages that live longer than their expiration time will be discarded (or dead - lettered).</doc> - <rule name="ttl-decrement"> - <doc> - If a message is transferred between brokers before delivery to a final subscriber the - ttl should be decremented before peer to peer transfer and both timestamp and expiration - should be cleared. - </doc> - </rule> - </field> - - <field name="timestamp" type="datetime" label="message timestamp"> - <doc> - The timestamp is set by the broker on arrival of the message. - </doc> - </field> - - <field name="expiration" type="datetime" label="message expiration time"> - <doc> - The expiration header assigned by the broker. After receiving the message the broker sets - expiration to the sum of the ttl specified in the publish command and the current time. - (ttl=expiration - timestamp) - </doc> - </field> - - <field name="exchange" type="exchange.name" label="originating exchange"> - <doc> - Identifies the exchange specified in the destination field of the message.transfer used to - publish the message. This MUST be set by the broker upon receipt of a message. - </doc> - </field> - - <field name="routing-key" type="str8" label="message routing key"> - <doc> - The value of the key determines to which queue the exchange will send the message. The way - in which keys are used to make this routing decision depends on the type of exchange to - which the message is sent. For example, a direct exchange will route a message to a queue - if that queue is bound to the exchange with a binding-key identical to the routing-key of - the message. - </doc> - </field> - - <field name="resume-id" type="resume-id" label="global id for message transfer"> - <doc> - When a resume-id is provided the recipient MAY use it to retain message data should the - session expire while the message transfer is still incomplete. - </doc> - </field> - - <field name="resume-ttl" type="uint64" label="ttl in ms for interrupted message data"> - <doc> - When a resume-ttl is provided the recipient MAY use it has a guideline for how long to - retain the partially complete data when a resume-id is specified. If no resume-id is - specified then this value should be ignored. - </doc> - </field> - </struct> - - <struct name="fragment-properties" size="4" code="0x2" pack="2"> - <doc> - These properties permit the transfer of message fragments. These may be used in conjunction - with byte level flow control to limit the rate at which large messages are received. Only - the first fragment carries the delivery-properties and message-properties. - - Syntactically each fragment appears as a complete message to the lower layers of the - protocol, however the model layer is required to treat all the fragments as a single - message. For example all fragments must be delivered to the same client. In pre-acquired - mode, no message fragments can be delivered by the broker until the entire message has been - received. - </doc> - - <field name="first" type="bit" default="1"> - <doc>True if this fragment contains the start of the message, false otherwise.</doc> - </field> - - <field name="last" type="bit" default="1"> - <doc>True if this fragment contains the end of the message, false otherwise.</doc> - </field> - - <field name="fragment-size" type="uint64"> - <doc>This field may optionally contain the size of the fragment.</doc> - </field> - </struct> - - <struct name="reply-to" size="2" pack="2"> - <doc>The reply-to domain provides a simple address structure for replying to to a message to a - destination within the same virtual-host.</doc> - <field name="exchange" type="exchange.name" label="the name of the exchange to reply to"/> - <field name="routing-key" type="str8" label="the routing-key to use when replying"/> - </struct> - - <struct name="message-properties" size="4" code="0x3" pack="2"> - <field name="content-length" type="uint64" label="length of the body segment in bytes"> - <doc> - The length of the body segment in bytes. - </doc> - </field> - - <field name="message-id" type="uuid" label="application message identifier"> - <doc> - Message-id is an optional property of UUID type which uniquely identifies a message within - the message system. The message producer is usually responsible for setting the - message-id. The server MAY discard a message as a duplicate if the value of the message-id - matches that of a previously received message. Duplicate messages MUST still be accepted - if transferred with an accept-mode of "explicit". - </doc> - - <rule name="unique"> - <doc> - A message-id MUST be unique within a given server instance. A message-id SHOULD be - globally unique (i.e. across different systems). - </doc> - </rule> - - <rule name="immutable"> - <doc> - A message ID is immutable. Once set, a message-id MUST NOT be changed or reassigned, - even if the message is replicated, resent or sent to multiple queues. - </doc> - </rule> - </field> - - <field name="correlation-id" type="vbin16" label="application correlation identifier"> - <doc> - This is a client-specific id that may be used to mark or identify messages between - clients. The server ignores this field. - </doc> - </field> - - <field name="reply-to" type="reply-to" label="destination to reply to"> - <doc> - The destination of any message that is sent in reply to this message. - </doc> - </field> - - <field name="content-type" type="str8" label="MIME content type"> - <doc> - The RFC-2046 MIME type for the message content (such as "text/plain"). This is set by the - originating client. - </doc> - </field> - - <field name="content-encoding" type="str8" label="MIME content encoding"> - <doc> - The encoding for character-based message content. This is set by the originating client. - Examples include UTF-8 and ISO-8859-15. - </doc> - </field> - - <field name="user-id" type="vbin16" label="creating user id"> - <doc> - The identity of the user responsible for producing the message. The client sets this - value, and it is authenticated by the broker. - </doc> - - <rule name="authentication"> - <doc> - The server MUST produce an unauthorized-access exception if the user-id field is set to - a principle for which the client is not authenticated. - </doc> - </rule> - </field> - - <field name="app-id" type="vbin16" label="creating application id"> - <doc> - The identity of the client application responsible for producing the message. - </doc> - </field> - - <field name="application-headers" type="map" label="application specific headers table"> - <doc> - This is a collection of user-defined headers or properties which may be set by the - producing client and retrieved by the consuming client. - </doc> - </field> - </struct> - - <domain name="flow-mode" type="uint8" label="the flow-mode for allocating flow credit"> - <enum> - <choice name="credit" value="0"> - <doc> - Credit based flow control. - </doc> - </choice> - - <choice name="window" value="1"> - <doc> - Window based flow control. - </doc> - </choice> - </enum> - </domain> - - <domain name="credit-unit" type="uint8" label="specifies the unit of credit balance"> - <enum> - <choice name="message" value="0"> - <doc>Indicates a value specified in messages.</doc> - </choice> - <choice name="byte" value="1"> - <doc>Indicates a value specified in bytes.</doc> - </choice> - </enum> - </domain> - - <!-- - Command: message.transfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="transfer" code="0x1" label="transfer a message"> - <doc> - This command transfers a message between two peers. When a client uses this command to - publish a message to a broker, the destination identifies a specific exchange. The message - will then be routed to queues as defined by the exchange configuration. - - The client may request a broker to transfer messages to it, from a particular queue, by - issuing a subscribe command. The subscribe command specifies the destination that the broker - should use for any resulting transfers. - </doc> - - <rule name="transactional-publish"> - <doc> - If a transfer to an exchange occurs within a transaction, then it is not available from - the queue until the transaction commits. It is not specified whether routing takes place - when the transfer is received or when the transaction commits. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - - <field name="destination" type="destination" label="message destination"> - <doc> - Specifies the destination to which the message is to be transferred. - </doc> - - <rule name="blank-destination"> - <doc> - The server MUST accept a blank destination to mean the default exchange. - </doc> - </rule> - - <exception name="nonexistent-exchange" error-code="not-found"> - <doc> - If the destination refers to an exchange that does not exist, the peer MUST raise a - session exception. - </doc> - </exception> - </field> - - <field name="accept-mode" type="accept-mode" required="true"> - <doc> - Indicates whether message.accept, session.complete, or nothing at all is required to - indicate successful transfer of the message. - </doc> - </field> - - <field name="acquire-mode" type="acquire-mode" required="true"> - <doc> - Indicates whether or not the transferred message has been acquired. - </doc> - </field> - - <segments> - <header> - <entry type="delivery-properties"/> - <entry type="fragment-properties"/> - <entry type="message-properties"/> - </header> - <body/> - </segments> - </command> - - <!-- - Command: message.accept - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="accept" code="0x2" label="reject a message"> - <doc> - Accepts the message. Once a transfer is accepted, the command-id may no longer be referenced - from other commands. - </doc> - - <rule name="acquisition"> - <doc> - The recipient MUST have acquired a message in order to accept it. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="transfers" type="session.commands" required="true"> - <doc> - Identifies the messages previously transferred that should be accepted. - </doc> - </field> - </command> - - <!-- - Command: message.reject - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="reject" code="0x3" label="reject a message"> - <doc> - Indicates that the message transfers are unprocessable in some way. A server may reject a - message if it is unroutable. A client may reject a message if it is invalid. A message may - be rejected for other reasons as well. Once a transfer is rejected, the command-id may no - longer be referenced from other commands. - </doc> - - <rule name="alternate-exchange"> - <doc> - When a client rejects a message, the server MUST deliver that message to the - alternate-exchange on the queue from which it was delivered. If no alternate-exchange is - defined for that queue the broker MAY discard the message. - </doc> - </rule> - - <rule name="acquisition"> - <doc> - The recipient MUST have acquired a message in order to reject it. If the message is not - acquired any reject MUST be ignored. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="transfers" type="session.commands" required="true"> - <doc> - Identifies the messages previously transferred that should be rejected. - </doc> - </field> - <field name="code" type="reject-code" required="true"> - <doc> - Code describing the reason for rejection. - </doc> - </field> - <field name="text" type="str8" label="informational text for message reject"> - <doc> - Text describing the reason for rejection. - </doc> - </field> - </command> - - <!-- - Command: message.release - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="release" code="0x4" label="release a message"> - <doc> - Release previously transferred messages. When acquired messages are released, they become - available for acquisition by any subscriber. Once a transfer is released, the command-id may - no longer be referenced from other commands. - </doc> - - <rule name="ordering"> - <doc> - Acquired messages that have been released MAY subsequently be delivered out of order. - Implementations SHOULD ensure that released messages keep their position with respect to - undelivered messages of the same priority. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MAY" /> - - <field name="transfers" type="session.commands" required="true"> - <doc> - Indicates the messages to be released. - </doc> - </field> - <field name="set-redelivered" type="bit" label="mark the released messages as redelivered"> - <doc> - By setting set-redelivered to true, any acquired messages released to a queue with this - command will be marked as redelivered on their next transfer from that queue. If this flag - is not set, then an acquired message will retain its original redelivered status on the - queue. Messages that are not acquired are unaffected by the value of this flag. - </doc> - </field> - </command> - - <!-- - Command: message.acquire - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="acquire" code="0x5" label="acquire messages for consumption"> - <doc> - Acquires previously transferred messages for consumption. The acquired ids (if any) are - sent via message.acquired. - </doc> - - <rule name="one-to-one"> - <doc> - Each acquire MUST produce exactly one message.acquired even if it is empty. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <field name="transfers" type="session.commands" required="true"> - <doc> - Indicates the messages to be acquired. - </doc> - </field> - - <result> - <struct name="acquired" size="4" code="0x4" pack="2" label="indicates acquired messages"> - <doc> - Identifies a set of previously transferred messages that have now been acquired. - </doc> - - <field name="transfers" type="session.commands" required="true"> - <doc> - Indicates the acquired messages. - </doc> - </field> - </struct> - </result> - </command> - - <!-- - Command: message.resume - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="resume" code="0x6" label="resume an interrupted message transfer"> - <doc> - This command resumes an interrupted transfer. The recipient should return the amount of - partially transferred data associated with the given resume-id, or zero if there is no data - at all. If a non-zero result is returned, the recipient should expect to receive message - fragment(s) containing the remainder of the interrupted message. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="destination" type="destination"> - <doc> - The destination to which the remaining message fragments are transferred. - </doc> - - <exception name="destination-not-found" error-code="not-found"> - <doc>If the destination does not exist, the recipient MUST close the session.</doc> - </exception> - </field> - - <field name="resume-id" type="resume-id" required="true"> - <doc> - The name of the transfer being resumed. - </doc> - - <rule name="unknown-resume-id"> - <doc>If the resume-id is not known, the recipient MUST return an offset of zero.</doc> - </rule> - </field> - - <result> - <struct name="message-resume-result" size="4" code="0x5" pack="2"> - <field name="offset" type="uint64"> - <doc> - Indicates the amount of data already transferred. - </doc> - </field> - </struct> - </result> - </command> - - <!-- - Command: message.subscribe - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="subscribe" code="0x7" label="start a queue subscription"> - <doc> This command asks the server to start a "subscription", which is a request for messages - from a specific queue. Subscriptions last as long as the session they were created on, or - until the client cancels them. </doc> - - <rule name="simultaneous-subscriptions"> - <doc> The server SHOULD support at least 16 subscriptions per queue, and ideally, impose no - limit except as defined by available resources. </doc> - <doc type="scenario"> Create a queue and create subscriptions on that queue until the server - closes the connection. Verify that the number of subscriptions created was at least - sixteen and report the total number. </doc> - </rule> - - <rule name="default-flow-mode"> - <doc> The default flow mode for new subscriptions is window-mode. </doc> - </rule> - - <exception name="queue-deletion" error-code="resource-deleted"> - <doc> - If the queue for this subscription is deleted, any subscribing sessions MUST be closed. - This exception may occur at any time after the subscription has been completed. - </doc> - </exception> - - <exception name="queue-not-found" error-code="not-found"> - <doc> If the queue for this subscription does not exist, then the subscribing session MUST - be closed. </doc> - </exception> - - <rule name="initial-credit"> - <doc> - Immediately after a subscription is created, the initial byte and message credit for that - destination is zero. - </doc> - </rule> - - <implement role="server" handle="MUST"/> - - <field name="queue" type="queue.name" required="true"> - <doc> Specifies the name of the subscribed queue. </doc> - </field> - - <field name="destination" type="destination" label="incoming message destination"> - <doc> The client specified name for the subscription. This is used as the destination for - all messages transferred from this subscription. The destination is scoped to the session. - </doc> - - <exception name="unique-subscriber-destination" error-code="not-allowed"> - <doc> The client MUST NOT specify a destination that refers to an existing subscription on - the same session. </doc> - <doc type="scenario"> Attempt to create two subscriptions on the same session with the - same non-empty destination. </doc> - </exception> - </field> - - <field name="accept-mode" type="accept-mode" required="true"> - <doc> The accept-mode to use for messages transferred from this subscription. </doc> - </field> - - <field name="acquire-mode" type="acquire-mode" required="true"> - <doc> The acquire-mode to use for messages transferred from this subscription. </doc> - </field> - - <field name="exclusive" type="bit" label="request exclusive access"> - <doc> Request an exclusive subscription. This prevents other subscribers from subscribing to - the queue. </doc> - - <exception name="in-use" error-code="resource-locked"> - <doc> The server MUST NOT grant an exclusive subscription to a queue that already has - subscribers. </doc> - <doc type="scenario"> Open two connections to a server, and in one connection create a - shared (non-exclusive) queue and then subscribe to the queue. In the second connection - attempt to subscribe to the same queue using the exclusive option. </doc> - </exception> - </field> - - <field name="resume-id" type="resume-id"> - <doc> Requests that the broker use the supplied resume-id when transferring messages for - this subscription. </doc> - </field> - - <field name="resume-ttl" type="uint64"> - <doc> Requested duration in milliseconds for the broker use as resume-ttl when transferring - messages for this subscription. </doc> - </field> - - <field name="arguments" type="map" label="arguments for vendor extensions"> - <doc> The syntax and semantics of these arguments depends on the providers implementation. - </doc> - </field> - </command> - - <!-- - Command: message.cancel - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="cancel" code="0x8" label="end a queue subscription"> - <doc> - This command cancels a subscription. This does not affect already delivered messages, but it - does mean the server will not send any more messages for that subscription. The client may - receive an arbitrary number of messages in between sending the cancel command and receiving - notification that the cancel command is complete. - </doc> - - <rule name="post-cancel-transfer-resolution"> - <doc> - Canceling a subscription MUST NOT affect pending transfers. A transfer made prior to - canceling transfers to the destination MUST be able to be accepted, released, acquired, or - rejected after the subscription is canceled. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <field name="destination" type="destination" required="true"> - <exception name="subscription-not-found" error-code="not-found"> - <doc> - If the subscription specified by the destination is not found, the server MUST close the - session. - </doc> - </exception> - </field> - </command> - - <!-- - Command: message.set-flow-mode - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="set-flow-mode" code="0x9" label="set the flow control mode"> - <doc> - Sets the mode of flow control used for a given destination to either window or credit based - flow control. - - With credit based flow control, the sender of messages continually maintains its current - credit balance with the recipient. The credit balance consists of two values, a message - count, and a byte count. Whenever message data is sent, both counts must be decremented. - If either value reaches zero, the flow of message data must stop. Additional credit is - received via the message.flow command. - - The sender MUST NOT send partial assemblies. This means that if there is not enough byte - credit available to send a complete message, the sender must either wait or use message - fragmentation (see the fragment-properties header struct) to send the first part of the - message data in a complete assembly. - - Window based flow control is identical to credit based flow control, however message - transfer completion implicitly grants a single unit of message credit, and the size of the - message in byte credits for each completed message transfer. Completion of the transfer - command with session.completed is the only way credit is implicitly updated; message.accept, - message.release, message.reject, tx.commit and tx.rollback have no effect on the outstanding - credit balances. - </doc> - - <rule name="byte-accounting"> - <doc> - The byte count is decremented by the payload size of each transmitted frame with segment - type header or body appearing within a message.transfer command. Note that the payload - size is the frame size less the frame header size. - </doc> - </rule> - - <rule name="mode-switching"> - <doc> - Mode switching may only occur if both the byte and message credit balance are zero. There - are three ways for a recipient of messages to be sure that the sender's credit balances - are zero: - - 1) The recipient may send a message.stop command to the sender. When the recipient - receives notification of completion for the message.stop command, it knows that the - sender's credit is zero. - - 2) The recipient may perform the same steps described in (1) with the message.flush - command substituted for the message.stop command. - - 3) Immediately after a subscription is created with message.subscribe, the credit for - that destination is zero. - </doc> - </rule> - - <rule name="default-flow-mode"> - <doc> - Prior to receiving an explicit set-flow-mode command, a peer MUST consider the flow-mode - to be window. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="destination" type="destination"/> - <field name="flow-mode" type="flow-mode" required="true"> - <doc> - The new flow control mode. - </doc> - </field> - </command> - - <!-- - Command: message.flow - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="flow" code="0xa" label="control message flow"> - <doc> - This command controls the flow of message data to a given destination. It is used by the - recipient of messages to dynamically match the incoming rate of message flow to its - processing or forwarding capacity. Upon receipt of this command, the sender must add "value" - number of the specified unit to the available credit balance for the specified destination. - A value of (0xFFFFFFFF) indicates an infinite amount of credit. This disables any limit for - the given unit until the credit balance is zeroed with message.stop or message.flush. - </doc> - - <!-- throws no-such-destination --> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="destination" type="destination"/> - <field name="unit" type="credit-unit" required="true"> - <doc> - The unit of value. - </doc> - </field> - <field name="value" type="uint32"> - <doc> - If the value is not set then this indicates an infinite amount of credit. - </doc> - </field> - </command> - - <!-- - Command: message.flush - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="flush" code="0xb" label="force the sending of available messages"> - <doc> - Forces the sender to exhaust his credit supply. The sender's credit will always be zero when - this command completes. The command completes when immediately available message data has - been transferred, or when the credit supply is exhausted. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="destination" type="destination"/> - </command> - - <!-- - Command: message.stop - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="stop" code="0xc" label="stop the sending of messages"> - <doc> - On receipt of this command, a producer of messages MUST set his credit to zero for the given - destination. When notifying of completion, credit MUST be zero and no further messages will - be sent until such a time as further credit is received. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <field name="destination" type="destination"/> - </command> - - </class> - - <!-- == Class: tx ============================================================================ --> - - <class name="tx" code="0x5" label="work with standard transactions"> - <doc> - Standard transactions provide so-called "1.5 phase commit". We can ensure that work is never - lost, but there is a chance of confirmations being lost, so that messages may be resent. - Applications that use standard transactions must be able to detect and ignore duplicate - messages. - </doc> - - <doc type="grammar"> - tx = C:SELECT - / C:COMMIT - / C:ROLLBACK - </doc> - - <!-- XXX: this isn't really a rule, as stated there is no way for - a client library to implement this --> - <rule name="duplicate-tracking"> - <doc> - An client using standard transactions SHOULD be able to track all messages received within a - reasonable period, and thus detect and reject duplicates of the same message. It SHOULD NOT - pass these to the application layer. - </doc> - </rule> - - <role name="server" implement="SHOULD" /> - - <!-- - Command: tx.select - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="select" code="0x1" label="select standard transaction mode"> - <doc> - This command sets the session to use standard transactions. The client must use this command - exactly once on a session before using the Commit or Rollback commands. - </doc> - - <exception name="exactly-once" error-code="illegal-state"> - <doc> - A client MUST NOT select standard transactions on a session that is already transactional. - </doc> - </exception> - - <exception name="no-dtx" error-code="illegal-state"> - <doc> - A client MUST NOT select standard transactions on a session that is already enlisted in a - distributed transaction. - </doc> - </exception> - - <exception name="explicit-accepts" error-code="not-allowed"> - <doc> - On a session on which tx.select has been issued, a client MUST NOT issue a - message.subscribe command with the accept-mode property set to any value other than - explicit. Similarly a tx.select MUST NOT be issued on a session on which a there is a non - cancelled subscriber with accept-mode of none. - </doc> - </exception> - - <implement role="server" handle="MUST" /> - </command> - - <!-- - Command: tx.commit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="commit" code="0x2" label="commit the current transaction"> - <doc> - This command commits all messages published and accepted in the current transaction. A - new transaction starts immediately after a commit. - </doc> - <doc> - In more detail, the commit acts on all messages which have been transferred from the Client - to the Server, and on all acceptances of messages sent from Server to Client. Since the - commit acts on commands sent in the same direction as the commit command itself, there is no - ambiguity on the scope of the commands being committed. Further, the commit will not be - completed until all preceding commands which it affects have been completed. - </doc> - <doc> - Since transactions act on explicit accept commands, the only valid accept-mode for message - subscribers is explicit. For transferring messages from Client to Server (publishing) all - accept-modes are permitted. - </doc> - - <exception name="select-required" error-code="illegal-state"> - <doc> - A client MUST NOT issue tx.commit on a session that has not been selected for standard - transactions with tx.select. - </doc> - </exception> - - - - <implement role="server" handle="MUST" /> - </command> - - <!-- - Command: tx.rollback - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="rollback" code="0x3" label="abandon the current transaction"> - <doc> - This command abandons the current transaction. In particular the transfers from Client to - Server (publishes) and accepts of transfers from Server to Client which occurred in the - current transaction are discarded. A new transaction starts immediately after a rollback. - </doc> - <doc> - In more detail, when a rollback is issued, any the effects of transfers which occurred from - Client to Server are discarded. The Server will issue completion notification for all such - transfers prior to the completion of the rollback. Similarly the effects of any - message.accept issued from Client to Server prior to the issuance of the tx.rollback will be - discarded; and notification of completion for all such commands will be issued before the - issuance of the completion for the rollback. - </doc> - <doc> - After the completion of the rollback, the client will still hold the messages which it has - not yet accepted (including those for which accepts were previously issued within the - transaction); i.e. the messages remain "acquired". If the Client wishes to release those - messages back to the Server, then appropriate message.release commands must be issued. - </doc> - - <exception name="select-required" error-code="illegal-state"> - <doc> - A client MUST NOT issue tx.rollback on a session that has not been selected for standard - transactions with tx.select. - </doc> - </exception> - - <implement role="server" handle="MUST" /> - </command> - - </class> - - <!-- == Class: dtx =========================================================================== --> - - <class name="dtx" code="0x6" label="Demarcates dtx branches"> - <doc> - This provides the X-Open XA distributed transaction protocol support. It allows a session - to be selected for use with distributed transactions, the transactional boundaries for work on - that session to be demarcated and allows the transaction manager to coordinate transaction - outcomes. - </doc> - - <doc type="grammar"> - dtx-demarcation = C:SELECT *demarcation - demarcation = C:START C:END - </doc> - - <doc type="grammar"> - dtx-coordination = *coordination - coordination = command - / outcome - / recovery - command = C:SET-TIMEOUT - / C:GET-TIMEOUT - outcome = one-phase-commit - / one-phase-rollback - / two-phase-commit - / two-phase-rollback - one-phase-commit = C:COMMIT - one-phase-rollback = C:ROLLBACK - two-phase-commit = C:PREPARE C:COMMIT - two-phase-rollback = C:PREPARE C:ROLLBACK - recovery = C:RECOVER *recovery-outcome - recovery-outcome = one-phase-commit - / one-phase-rollback - / C:FORGET - - </doc> - - <rule name="transactionality"> - <doc> - Enabling XA transaction support on a session requires that the server MUST manage - transactions demarcated by start-end blocks. That is to say that on this XA-enabled session, - work undergone within transactional blocks is performed on behalf a transaction branch - whereas work performed outside of transactional blocks is NOT transactional. - </doc> - </rule> - - <role name="server" implement="MAY" /> - <role name="client" implement="MAY" /> - - <!-- XA domains --> - - <domain name="xa-status" type="uint16" label="XA return codes"> - <enum> - <choice name="xa-ok" value="0"> - <doc> - Normal execution completion (no error). - </doc> - </choice> - - <choice name="xa-rbrollback" value="1"> - <doc> - The rollback was caused for an unspecified reason. - </doc> - </choice> - - <choice name="xa-rbtimeout" value="2"> - <doc> - A transaction branch took too long. - </doc> - </choice> - - <choice name="xa-heurhaz" value="3"> - <doc> - The transaction branch may have been heuristically completed. - </doc> - </choice> - - <choice name="xa-heurcom" value="4"> - <doc> - The transaction branch has been heuristically committed. - </doc> - </choice> - - <choice name="xa-heurrb" value="5"> - <doc> - The transaction branch has been heuristically rolled back. - </doc> - </choice> - - <choice name="xa-heurmix" value="6"> - <doc> - The transaction branch has been heuristically committed and rolled back. - </doc> - </choice> - - <choice name="xa-rdonly" value="7"> - <doc> - The transaction branch was read-only and has been committed. - </doc> - </choice> - </enum> - </domain> - - <struct name="xa-result" size="4" code="0x1" pack="2"> - <field name="status" type="xa-status" required="true"/> - </struct> - - <!-- Struct for xid --> - - <struct name="xid" size="4" code="0x4" pack="2" label="dtx branch identifier"> - <doc> - An xid uniquely identifies a transaction branch. - </doc> - - <field name="format" type="uint32" label="implementation specific format code" - required="true"/> - <field name="global-id" type="vbin8" label="global transaction id" required="true"/> - <field name="branch-id" type="vbin8" label="branch qualifier" required="true"/> - </struct> - - <!-- - Command: dtx.select - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="select" code="0x1" label="Select dtx mode"> - <doc> - This command sets the session to use distributed transactions. The client must use this - command at least once on a session before using XA demarcation operations. - </doc> - - <implement role="server" handle="MAY" /> - </command> - - <!-- - Command: dtx.start - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="start" code="0x2" label="Start a dtx branch"> - <doc> - This command is called when messages should be produced and consumed on behalf a transaction - branch identified by xid. - </doc> - - <exception name="illegal-state" error-code="illegal-state"> - <doc> - If the command is invoked in an improper context (see class grammar) then the server MUST - send a session exception. - </doc> - </exception> - - <exception name="already-known" error-code="not-allowed"> - <doc> - If neither join nor resume is specified is specified and the transaction branch specified - by xid has previously been seen then the server MUST raise an exception. - </doc> - </exception> - - <exception name="join-and-resume" error-code="not-allowed"> - <doc> - If join and resume are specified then the server MUST raise an exception. - </doc> - </exception> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch to be started. - </doc> - - <exception name="unknown-xid" error-code="not-allowed"> - <doc> - If xid is already known by the broker then the server MUST raise an exception. - </doc> - </exception> - </field> - - <field name="join" type="bit" label="Join with existing xid flag"> - <doc> - Indicate whether this is joining an already associated xid. Indicate that the start - applies to joining a transaction previously seen. - </doc> - - <exception name="unsupported" error-code="not-implemented"> - <doc> - If the broker does not support join the server MUST raise an exception. - </doc> - </exception> - </field> - - <field name="resume" type="bit" label="Resume flag"> - <doc> - Indicate that the start applies to resuming a suspended transaction branch specified. - </doc> - </field> - - <result type="xa-result"> - <doc> - This confirms to the client that the transaction branch is started or specify the error - condition. - - The value of this field may be one of the following constants: - - xa-ok: Normal execution. - - xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified - reason. - - xa-rbtimeout: The work represented by this transaction branch took too long. - </doc> - </result> - </command> - - <!-- - Command: dtx.end - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="end" code="0x3" label="End a dtx branch"> - <doc> - This command is called when the work done on behalf a transaction branch finishes or needs - to be suspended. - </doc> - - <exception name="illegal-state" error-code="illegal-state"> - <doc> - If the command is invoked in an improper context (see class grammar) then the server MUST - raise an exception. - </doc> - </exception> - - <exception name="suspend-and-fail" error-code="not-allowed"> - <doc> - If suspend and fail are specified then the server MUST raise an exception. - </doc> - </exception> - - <rule name="success"> - <doc> - If neither fail nor suspend are specified then the portion of work has completed - successfully. - </doc> - </rule> - - <rule name="session-closed"> - <doc> - When a session is closed then the currently associated transaction branches MUST be marked - rollback-only. - </doc> - </rule> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch to be ended. - </doc> - - <exception name="not-associated" error-code="illegal-state"> - <doc> - The session MUST be currently associated with the given xid (through an earlier start - call with the same xid). - </doc> - </exception> - </field> - - <field name="fail" type="bit" label="Failure flag"> - <doc> - If set, indicates that this portion of work has failed; otherwise this portion of work has - completed successfully. - </doc> - - <rule name="failure"> - <doc> - An implementation MAY elect to roll a transaction back if this failure notification is - received. Should an implementation elect to implement this behavior, and this bit is - set, then then the transaction branch SHOULD be marked as rollback-only and the end - result SHOULD have the xa-rbrollback status set. - </doc> - </rule> - </field> - - <field name="suspend" type="bit" label="Temporary suspension flag"> - <doc> - Indicates that the transaction branch is temporarily suspended in an incomplete state. - </doc> - - <rule name="resume"> - <doc> - The transaction context is in a suspended state and must be resumed via the start - command with resume specified. - </doc> - </rule> - - </field> - - <result type="xa-result"> - <doc> - This command confirms to the client that the transaction branch is ended or specify the - error condition. - - The value of this field may be one of the following constants: - - xa-ok: Normal execution. - - xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified - reason. If an implementation chooses to implement rollback-on-failure behavior, then - this value should be selected if the dtx.end.fail bit was set. - - xa-rbtimeout: The work represented by this transaction branch took too long. - </doc> - </result> - </command> - - <!-- - Command: dtx.commit - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="commit" code="0x4" label="Commit work on dtx branch"> - <doc> - Commit the work done on behalf a transaction branch. This command commits the work - associated with xid. Any produced messages are made available and any consumed messages are - discarded. - </doc> - - <exception name="illegal-state" error-code="illegal-state"> - <doc> - If the command is invoked in an improper context (see class grammar) then the server MUST - raise an exception. - </doc> - </exception> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch to be committed. - </doc> - - <exception name="unknown-xid" error-code="not-found"> - <doc> - If xid is unknown (the transaction branch has not been started or has already been - ended) then the server MUST raise an exception. - </doc> - </exception> - - <exception name="not-disassociated" error-code="illegal-state"> - <doc> - If this command is called when xid is still associated with a session then the server - MUST raise an exception. - </doc> - </exception> - </field> - - <field name="one-phase" type="bit" label="One-phase optimization flag"> - <doc> - Used to indicate whether one-phase or two-phase commit is used. - </doc> - - <exception name="one-phase" error-code="illegal-state"> - <doc> - The one-phase bit MUST be set if a commit is sent without a preceding prepare. - </doc> - </exception> - - <exception name="two-phase" error-code="illegal-state"> - <doc> - The one-phase bit MUST NOT be set if the commit has been preceded by prepare. - </doc> - </exception> - </field> - - <result type="xa-result"> - <doc> - This confirms to the client that the transaction branch is committed or specify the - error condition. - - The value of this field may be one of the following constants: - - xa-ok: Normal execution - - xa-heurhaz: Due to some failure, the work done on behalf of the specified transaction - branch may have been heuristically completed. - - xa-heurcom: Due to a heuristic decision, the work done on behalf of the specified - transaction branch was committed. - - xa-heurrb: Due to a heuristic decision, the work done on behalf of the specified - transaction branch was rolled back. - - xa-heurmix: Due to a heuristic decision, the work done on behalf of the specified - transaction branch was partially committed and partially rolled back. - - xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified - reason. - - xa-rbtimeout: The work represented by this transaction branch took too long. - </doc> - </result> - </command> - - <!-- - Command: dtx.forget - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="forget" code="0x5" label="Discard dtx branch"> - <doc> - This command is called to forget about a heuristically completed transaction branch. - </doc> - - <exception name="illegal-state" error-code="illegal-state"> - <doc> - If the command is invoked in an improper context (see class grammar) then the server MUST - raise an exception. - </doc> - </exception> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch to be forgotten. - </doc> - - <exception name="unknown-xid" error-code="not-found"> - <doc> - If xid is unknown (the transaction branch has not been started or has already been - ended) then the server MUST raise an exception. - </doc> - </exception> - - <exception name="not-disassociated" error-code="illegal-state"> - <doc> - If this command is called when xid is still associated with a session then the server - MUST raise an exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: dtx.get-timeout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="get-timeout" code="0x6" label="Obtain dtx timeout in seconds"> - <doc> - This command obtains the current transaction timeout value in seconds. If set-timeout was - not used prior to invoking this command, the return value is the default timeout; otherwise, - the value used in the previous set-timeout call is returned. - </doc> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch for getting the timeout. - </doc> - - <exception name="unknown-xid" error-code="not-found"> - <doc> - If xid is unknown (the transaction branch has not been started or has already been - ended) then the server MUST raise an exception. - </doc> - </exception> - </field> - - <result> - <struct name="get-timeout-result" size="4" code="0x2" pack="2"> - <doc> Returns the value of the timeout last specified through set-timeout. </doc> - - <field name="timeout" type="uint32" label="The current transaction timeout value" - required="true"> - <doc> The current transaction timeout value in seconds. </doc> - </field> - </struct> - </result> - </command> - - <!-- - Command: dtx.prepare - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="prepare" code="0x7" label="Prepare a dtx branch"> - <doc> - This command prepares for commitment any message produced or consumed on behalf of xid. - </doc> - - <exception name="illegal-state" error-code="illegal-state"> - <doc> - If the command is invoked in an improper context (see class grammar) then the server MUST - raise an exception. - </doc> - </exception> - - <rule name="obligation-1"> - <doc> - Once this command successfully returns it is guaranteed that the transaction branch may be - either committed or rolled back regardless of failures. - </doc> - </rule> - - <rule name="obligation-2"> - <doc> - The knowledge of xid cannot be erased before commit or rollback complete the branch. - </doc> - </rule> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch that can be prepared. - </doc> - - <exception name="unknown-xid" error-code="not-found"> - <doc> - If xid is unknown (the transaction branch has not been started or has already been - ended) then the server MUST raise an exception. - </doc> - </exception> - - <exception name="not-disassociated" error-code="illegal-state"> - <doc> - If this command is called when xid is still associated with a session then the server - MUST raise an exception. - </doc> - </exception> - </field> - - <result type="xa-result"> - <doc> - This command confirms to the client that the transaction branch is prepared or specify the - error condition. - - The value of this field may be one of the following constants: - - xa-ok: Normal execution. - - xa-rdonly: The transaction branch was read-only and has been committed. - - xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified - reason. - - xa-rbtimeout: The work represented by this transaction branch took too long. - </doc> - </result> - </command> - - <!-- - Command: dtx.recover - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="recover" code="0x8" label="Get prepared or completed xids"> - <doc> - This command is called to obtain a list of transaction branches that are in a prepared or - heuristically completed state. - </doc> - - <implement role="server" handle="MAY" /> - - <result> - <struct name="recover-result" size="4" code="0x3" pack="2"> - <doc> - Returns to the client a table with single item that is a sequence of transaction xids - that are in a prepared or heuristically completed state. - </doc> - - <field name="in-doubt" type="array" label="array of xids to be recovered" required="true"> - <doc> Array containing the xids to be recovered (xids that are in a prepared or - heuristically completed state). </doc> - - </field> - </struct> - </result> - </command> - - <!-- - Command: dtx.rollback - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="rollback" code="0x9" label="Rollback a dtx branch"> - <doc> - This command rolls back the work associated with xid. Any produced messages are discarded - and any consumed messages are re-enqueued. - </doc> - - <exception name="illegal-state" error-code="illegal-state"> - <doc> - If the command is invoked in an improper context (see class grammar) then the server MUST - raise an exception. - </doc> - </exception> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch that can be rolled back. - </doc> - - <exception name="unknown-xid" error-code="not-found"> - <doc> - If xid is unknown (the transaction branch has not been started or has already been - ended) then the server MUST raise an exception. - </doc> - </exception> - - <exception name="not-disassociated" error-code="illegal-state"> - <doc> - If this command is called when xid is still associated with a session then the server - MUST raise an exception. - </doc> - </exception> - </field> - - <result type="xa-result"> - <doc> - This command confirms to the client that the transaction branch is rolled back or specify - the error condition. - - The value of this field may be one of the following constants: - - xa-ok: Normal execution - - xa-heurhaz: Due to some failure, the work done on behalf of the specified transaction - branch may have been heuristically completed. - - xa-heurcom: Due to a heuristic decision, the work done on behalf of the specified - transaction branch was committed. - - xa-heurrb: Due to a heuristic decision, the work done on behalf of the specified - transaction branch was rolled back. - - xa-heurmix: Due to a heuristic decision, the work done on behalf of the specified - transaction branch was partially committed and partially rolled back. - - xa-rbrollback: The broker marked the transaction branch rollback-only for an unspecified - reason. - - xa-rbtimeout: The work represented by this transaction branch took too long. - </doc> - </result> - </command> - - <!-- - Command: dtx.set-timeout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="set-timeout" code="0xa" label="Set dtx timeout value"> - <doc> - Sets the specified transaction branch timeout value in seconds. - </doc> - - <rule name="effective"> - <doc> - Once set, this timeout value is effective until this command is reinvoked with a different - value. - </doc> - </rule> - - <rule name="reset"> - <doc> - A value of zero resets the timeout value to the default value. - </doc> - </rule> - - <implement role="server" handle="MAY" /> - - <field name="xid" type="xid" label="Transaction xid" required="true"> - <doc> - Specifies the xid of the transaction branch for setting the timeout. - </doc> - - <exception name="unknown-xid" error-code="not-found"> - <doc> - If xid is unknown (the transaction branch has not been started or has already been - ended) then the server MUST raise an exception. - </doc> - </exception> - - </field> - - <field name="timeout" type="uint32" label="Dtx timeout in seconds" required="true"> - <doc> - The transaction timeout value in seconds. - </doc> - </field> - </command> - - </class> - - <!-- == Class: exchange ====================================================================== --> - - <class name="exchange" code="0x7" label="work with exchanges"> - <doc> - Exchanges match and distribute messages across queues. Exchanges can be configured in the - server or created at runtime. - </doc> - - <doc type="grammar"> - exchange = C:DECLARE - / C:DELETE - / C:QUERY - </doc> - - <rule name="required-types"> - <doc> - The server MUST implement these standard exchange types: fanout, direct. - </doc> - <doc type="scenario"> - Client attempts to declare an exchange with each of these standard types. - </doc> - </rule> - - <rule name="recommended-types"> - <doc> - The server SHOULD implement these standard exchange types: topic, headers. - </doc> - <doc type="scenario"> - Client attempts to declare an exchange with each of these standard types. - </doc> - </rule> - - <rule name="required-instances"> - <doc> - The server MUST, in each virtual host, pre-declare an exchange instance for each standard - exchange type that it implements, where the name of the exchange instance, if defined, is - "amq." followed by the exchange type name. - - The server MUST, in each virtual host, pre-declare at least two direct exchange instances: - one named "amq.direct", the other with no public name that serves as a default exchange for - publish commands (such as message.transfer). - </doc> - <doc type="scenario"> - Client creates a temporary queue and attempts to bind to each required exchange instance - ("amq.fanout", "amq.direct", "amq.topic", and "amq.headers" if those types are defined). - </doc> - </rule> - - <rule name="default-exchange"> - <doc> - The server MUST pre-declare a direct exchange with no public name to act as the default - exchange for content publish commands (such as message.transfer) and for default queue - bindings. - </doc> - <doc type="scenario"> - Client checks that the default exchange is active by publishing a message with a suitable - routing key but without specifying the exchange name, then ensuring that the message arrives - in the queue correctly. - </doc> - </rule> - - <rule name="default-access"> - <doc> - The default exchange MUST NOT be accessible to the client except by specifying an empty - exchange name in a content publish command (such as message.transfer). That is, the server - must not let clients explicitly bind, unbind, delete, or make any other reference to this - exchange. - </doc> - </rule> - - <rule name="extensions"> - <doc> - The server MAY implement other exchange types as wanted. - </doc> - </rule> - - <role name="server" implement="MUST" /> - <role name="client" implement="MUST" /> - - <domain name="name" type="str8" label="exchange name"> - <doc> - The exchange name is a client-selected string that identifies the exchange for publish - commands. Exchange names may consist of any mixture of digits, letters, and underscores. - Exchange names are scoped by the virtual host. - </doc> - </domain> - - <!-- - Command: exchange.declare - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="declare" code="0x1" label="verify exchange exists, create if needed"> - <doc> - This command creates an exchange if it does not already exist, and if the exchange exists, - verifies that it is of the correct and expected class. - </doc> - - <rule name="minimum"> - <doc> - The server SHOULD support a minimum of 16 exchanges per virtual host and ideally, impose - no limit except as defined by available resources. - </doc> - <doc type="scenario"> - The client creates as many exchanges as it can until the server reports an error; the - number of exchanges successfully created must be at least sixteen. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <field name="exchange" type="name" required="true"> - <exception name="reserved-names" error-code="not-allowed"> - <doc> - Exchange names starting with "amq." are reserved for pre-declared and standardized - exchanges. The client MUST NOT attempt to create an exchange starting with "amq.". - </doc> - </exception> - - <exception name="exchange-name-required" error-code="invalid-argument"> - <doc> - The name of the exchange MUST NOT be a blank or empty string. - </doc> - </exception> - </field> - - <field name="type" type="str8" label="exchange type" required="true"> - <doc> - Each exchange belongs to one of a set of exchange types implemented by the server. The - exchange types define the functionality of the exchange - i.e. how messages are routed - through it. It is not valid or meaningful to attempt to change the type of an existing - exchange. - </doc> - - <exception name="typed" error-code="not-allowed"> - <doc> - Exchanges cannot be redeclared with different types. The client MUST NOT attempt to - redeclare an existing exchange with a different type than used in the original - exchange.declare command. - </doc> - </exception> - - <exception name="exchange-type-not-found" error-code="not-found"> - <doc> - If the client attempts to create an exchange which the server does not recognize, an - exception MUST be sent. - </doc> - </exception> - </field> - - <field name="alternate-exchange" type="name" label= "exchange name for unroutable messages"> - <doc> - In the event that a message cannot be routed, this is the name of the exchange to which - the message will be sent. Messages transferred using message.transfer will be routed to - the alternate-exchange only if they are sent with the "none" accept-mode, and the - discard-unroutable delivery property is set to false, and there is no queue to route to - for the given message according to the bindings on this exchange. - </doc> - - <rule name="empty-name"> - <doc> - If alternate-exchange is not set (its name is an empty string), unroutable messages - that would be sent to the alternate-exchange MUST be dropped silently. - </doc> - </rule> - - <exception name="pre-existing-exchange" error-code="not-allowed"> - <doc> - If the alternate-exchange is not empty and if the exchange already exists with a - different alternate-exchange, then the declaration MUST result in an exception. - </doc> - </exception> - - <rule name="double-failure"> - <doc> - A message which is being routed to a alternate exchange, MUST NOT be re-routed to a - secondary alternate exchange if it fails to route in the primary alternate exchange. - After such a failure, the message MUST be dropped. This prevents looping. - </doc> - </rule> - </field> - - <field name="passive" type="bit" label="do not create exchange"> - <doc> - If set, the server will not create the exchange. The client can use this to check whether - an exchange exists without modifying the server state. - </doc> - <exception name="not-found" error-code="not-found"> - <doc> - If set, and the exchange does not already exist, the server MUST raise an exception. - </doc> - </exception> - </field> - - <field name="durable" type="bit" label="request a durable exchange"> - <doc> - If set when creating a new exchange, the exchange will be marked as durable. Durable - exchanges remain active when a server restarts. Non-durable exchanges (transient - exchanges) are purged if/when a server restarts. - </doc> - - <rule name="support"> - <doc> - The server MUST support both durable and transient exchanges. - </doc> - </rule> - - <rule name="sticky"> - <doc> - The server MUST ignore the durable field if the exchange already exists. - </doc> - </rule> - </field> - - <field name="auto-delete" type="bit" label="auto-delete when unused"> - <doc> - If set, the exchange is deleted automatically when there remain no bindings between the - exchange and any queue. Such an exchange will not be automatically deleted until at least - one binding has been made to prevent the immediate deletion of the exchange upon creation. - </doc> - <rule name="sticky"> - <doc> - The server MUST ignore the auto-delete field if the exchange already exists. - </doc> - </rule> - </field> - - <field name="arguments" type="map" label="arguments for declaration"> - <doc> - A set of arguments for the declaration. The syntax and semantics of these arguments - depends on the server implementation. This field is ignored if passive is 1. - </doc> - - <exception name="unknown-argument" error-code="not-implemented"> - <doc> - If the arguments field contains arguments which are not understood by the server, - it MUST raise an exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: exchange.delete - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="delete" code="0x2" label="delete an exchange"> - <doc> - This command deletes an exchange. When an exchange is deleted all queue bindings on the - exchange are cancelled. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="exchange" type="name" required="true"> - <exception name="exists" error-code="not-found"> - <doc> - The client MUST NOT attempt to delete an exchange that does not exist. - </doc> - </exception> - - <exception name="exchange-name-required" error-code="invalid-argument"> - <doc> - The name of the exchange MUST NOT be a missing or empty string. - </doc> - </exception> - - <exception name="used-as-alternate" error-code="not-allowed"> - <doc> - An exchange MUST NOT be deleted if it is in use as an alternate-exchange by a queue or - by another exchange. - </doc> - </exception> - - </field> - - <field name="if-unused" type="bit" label="delete only if unused"> - <doc> - If set, the server will only delete the exchange if it has no queue bindings. If the - exchange has queue bindings the server does not delete it but raises an exception - instead. - </doc> - <exception name="exchange-in-use" error-code="precondition-failed"> - <doc> - If the exchange has queue bindings, and the if-unused flag is set, the server MUST NOT - delete the exchange, but MUST raise and exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: exchange.query - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="query" code="0x3" label="request information about an exchange"> - <doc> - This command is used to request information on a particular exchange. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="name" type="str8" label="the exchange name"> - <doc> - The name of the exchange for which information is requested. If not specified explicitly - the default exchange is implied. - </doc> - </field> - - <result> - <struct name="exchange-query-result" size="4" code="0x1" pack="2"> - <doc> - This is sent in response to a query request and conveys information on a particular - exchange. - </doc> - - <field name="type" type="str8" label="indicate the exchange type"> - <doc> - The type of the exchange. Will be empty if the exchange is not found. - </doc> - </field> - - <field name="durable" type="bit" label="indicate the durability"> - <doc> - The durability of the exchange, i.e. if set the exchange is durable. Will not be set - if the exchange is not found. - </doc> - </field> - - <field name="not-found" type="bit" label="indicate an unknown exchange"> - <doc> - If set, the exchange for which information was requested is not known. - </doc> - </field> - - <field name="arguments" type="map" label="other unspecified exchange properties"> - <doc> - A set of properties of the exchange whose syntax and semantics depends on the server - implementation. Will be empty if the exchange is not found. - </doc> - </field> - </struct> - </result> - </command> - - <!-- - Command: exchange.bind - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="bind" code="0x4" label="bind queue to an exchange"> - <doc> This command binds a queue to an exchange. Until a queue is bound it will not receive - any messages. In a classic messaging model, store-and-forward queues are bound to a direct - exchange and subscription queues are bound to a topic exchange. </doc> - - <rule name="duplicates"> - <doc> - A server MUST ignore duplicate bindings - that is, two or more bind commands with the - same exchange, queue, and binding-key - without treating these as an error. The value of - the arguments used for the binding MUST NOT be altered by subsequent binding requests. - </doc> - <doc type="scenario"> - A client binds a named queue to an exchange. The client then repeats the bind (with - identical exchange, queue, and binding-key). The second binding should use a different - value for the arguments field. - </doc> - </rule> - - <rule name="durable-exchange"> - <doc> Bindings between durable queues and durable exchanges are automatically durable and - the server MUST restore such bindings after a server restart. </doc> - <doc type="scenario"> A server creates a named durable queue and binds it to a durable - exchange. The server is restarted. The client then attempts to use the queue/exchange - combination. </doc> - </rule> - - <rule name="binding-count"> - <doc> The server SHOULD support at least 4 bindings per queue, and ideally, impose no limit - except as defined by available resources. </doc> - <doc type="scenario"> A client creates a named queue and attempts to bind it to 4 different - exchanges. </doc> - </rule> - - <rule name="multiple-bindings"> - <doc> Where more than one binding exists between a particular exchange instance and a - particular queue instance any given message published to that exchange should be delivered - to that queue at most once, regardless of how many distinct bindings match. </doc> - <doc type="scenario"> A client creates a named queue and binds it to the same topic exchange - at least three times using intersecting binding-keys (for example, "animals.*", - "animals.dogs.*", "animal.dogs.chihuahua"). Verify that a message matching all the - bindings (using previous example, routing key = "animal.dogs.chihuahua") is delivered once - only. </doc> - </rule> - - <implement role="server" handle="MUST"/> - - <field name="queue" type="queue.name" required="true"> - <doc> Specifies the name of the queue to bind. </doc> - - <exception name="empty-queue" error-code="invalid-argument"> - <doc> A client MUST NOT be allowed to bind a non-existent and unnamed queue (i.e. empty - queue name) to an exchange. </doc> - <doc type="scenario"> A client attempts to bind with an unnamed (empty) queue name to an - exchange. </doc> - </exception> - - <exception name="queue-existence" error-code="not-found"> - <doc> A client MUST NOT be allowed to bind a non-existent queue (i.e. not previously - declared) to an exchange. </doc> - <doc type="scenario"> A client attempts to bind an undeclared queue name to an exchange. - </doc> - </exception> - </field> - - <field name="exchange" type="name" label="name of the exchange to bind to" required="true"> - <exception name="exchange-existence" error-code="not-found"> - <doc> A client MUST NOT be allowed to bind a queue to a non-existent exchange. </doc> - <doc type="scenario"> A client attempts to bind a named queue to a undeclared exchange. - </doc> - </exception> - - <exception name="exchange-name-required" error-code="invalid-argument"> - <doc> The name of the exchange MUST NOT be a blank or empty string. </doc> - </exception> - </field> - - <field name="binding-key" type="str8" - label="identifies a binding between a given exchange and queue" required="true"> - <doc> The binding-key uniquely identifies a binding between a given (exchange, queue) pair. - Depending on the exchange configuration, the binding key may be matched against the - message routing key in order to make routing decisions. The match algorithm depends on the - exchange type. Some exchange types may ignore the binding key when making routing - decisions. Refer to the specific exchange type documentation. The meaning of an empty - binding key depends on the exchange implementation. </doc> - </field> - - <field name="arguments" type="map" label="arguments for binding"> - <doc> A set of arguments for the binding. The syntax and semantics of these arguments - depends on the exchange class. </doc> - - <exception name="unknown-argument" error-code="not-implemented"> - <doc> If the arguments field contains arguments which are not understood by the server, it - MUST raise an exception. </doc> - </exception> - </field> - </command> - - <!-- - Command: exchange.unbind - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="unbind" code="0x5" label="unbind a queue from an exchange"> - <doc> - This command unbinds a queue from an exchange. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="queue" type="queue.name" required="true"> - <doc> - Specifies the name of the queue to unbind. - </doc> - <exception name="non-existent-queue" error-code="not-found"> - <doc> - If the queue does not exist the server MUST raise an exception. - </doc> - </exception> - </field> - - <field name="exchange" type="name" required="true"> - <doc> - The name of the exchange to unbind from. - </doc> - - <exception name="non-existent-exchange" error-code="not-found"> - <doc> - If the exchange does not exist the server MUST raise an exception. - </doc> - </exception> - - <exception name="exchange-name-required" error-code="invalid-argument"> - <doc> - The name of the exchange MUST NOT be a blank or empty string. - </doc> - </exception> - </field> - - <field name="binding-key" type="str8" label="the key of the binding" required="true"> - <doc> - Specifies the binding-key of the binding to unbind. - </doc> - - <exception name="non-existent-binding-key" error-code="not-found"> - <doc> - If there is no matching binding-key the server MUST raise an exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: exchange.bound - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="bound" code="0x6" label="request information about bindings to an exchange"> - <doc> - This command is used to request information on the bindings to a particular exchange. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="exchange" type="str8" label="the exchange name"> - <doc> - The name of the exchange for which binding information is being requested. If not - specified explicitly the default exchange is implied. - </doc> - </field> - - <field name="queue" type="str8" label="a queue name" required="true"> - <doc> - If populated then determine whether the given queue is bound to the exchange. - </doc> - </field> - - <field name="binding-key" type="str8" label="a binding-key"> - <doc> - If populated defines the binding-key of the binding of interest, if not populated the - request will ignore the binding-key on bindings when searching for a match. - </doc> - </field> - - <field name="arguments" type="map" label="a set of binding arguments"> - <doc> - If populated defines the arguments of the binding of interest if not populated the request - will ignore the arguments on bindings when searching for a match - </doc> - </field> - - <result> - <struct name="exchange-bound-result" size="4" code="0x2" pack="2"> - <field name="exchange-not-found" type="bit" label="indicate an unknown exchange"> - <doc> - If set, the exchange for which information was requested is not known. - </doc> - </field> - - <field name="queue-not-found" type="bit" label="indicate an unknown queue"> - <doc> - If set, the queue specified is not known. - </doc> - </field> - - <field name="queue-not-matched" type="bit" label="indicate no matching queue"> - <doc> - A bit which if set indicates that no binding was found from the specified exchange to - the specified queue. - </doc> - </field> - - <field name="key-not-matched" type="bit" label="indicate no matching binding-key"> - <doc> - A bit which if set indicates that no binding was found from the specified exchange - with the specified binding-key. - </doc> - </field> - - <field name="args-not-matched" type="bit" label="indicate no matching arguments"> - <doc> - A bit which if set indicates that no binding was found from the specified exchange - with the specified arguments. - </doc> - </field> - </struct> - </result> - </command> - - </class> - - <!-- == Class: queue ========================================================================= --> - - <class name="queue" code="0x8" label="work with queues"> - <doc> - Queues store and forward messages. Queues can be configured in the server or created at - runtime. Queues must be attached to at least one exchange in order to receive messages from - publishers. - </doc> - - <doc type="grammar"> - queue = C:DECLARE - / C:BIND - / C:PURGE - / C:DELETE - / C:QUERY - / C:UNBIND - </doc> - - <rule name="any-content"> - <doc> - A server MUST allow any content class to be sent to any queue, in any mix, and queue and - deliver these content classes independently. Note that all commands that fetch content off - queues are specific to a given content class. - </doc> - <doc type="scenario"> - Client creates an exchange of each standard type and several queues that it binds to each - exchange. It must then successfully send each of the standard content types to each of the - available queues. - </doc> - </rule> - - <role name="server" implement="MUST" /> - <role name="client" implement="MUST" /> - - <domain name="name" type="str8" label="queue name"> - <doc> - The queue name identifies the queue within the virtual host. Queue names must have a length - of between 1 and 255 characters inclusive, must start with a digit, letter or underscores - ('_') character, and must be otherwise encoded in UTF-8. - </doc> - </domain> - - <!-- - Command: queue.declare - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="declare" code="0x1" label="declare queue"> - <doc> - This command creates or checks a queue. When creating a new queue the client can specify - various properties that control the durability of the queue and its contents, and the level - of sharing for the queue. - </doc> - - <rule name="default-binding"> - <doc> - The server MUST create a default binding for a newly-created queue to the default - exchange, which is an exchange of type 'direct' and use the queue name as the binding-key. - </doc> - <doc type="scenario"> - Client creates a new queue, and then without explicitly binding it to an exchange, - attempts to send a message through the default exchange binding, i.e. publish a message to - the empty exchange, with the queue name as binding-key. - </doc> - </rule> - - <rule name="minimum-queues"> - <doc> - The server SHOULD support a minimum of 256 queues per virtual host and ideally, impose no - limit except as defined by available resources. - </doc> - <doc type="scenario"> - Client attempts to create as many queues as it can until the server reports an error. The - resulting count must at least be 256. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <field name="queue" type="name" required="true"> - <exception name="reserved-prefix" error-code="not-allowed"> - <doc> - Queue names starting with "amq." are reserved for pre-declared and standardized server - queues. A client MUST NOT attempt to declare a queue with a name that starts with "amq." - and the passive option set to zero. - </doc> - <doc type="scenario"> - A client attempts to create a queue with a name starting with "amq." and with the - passive option set to zero. - </doc> - </exception> - </field> - - <field name="alternate-exchange" type="exchange.name" - label= "exchange name for messages with exceptions"> - <doc> - The alternate-exchange field specifies how messages on this queue should be treated when - they are rejected by a subscriber, or when they are orphaned by queue deletion. When - present, rejected or orphaned messages MUST be routed to the alternate-exchange. In all - cases the messages MUST be removed from the queue. - </doc> - - <exception name="pre-existing-exchange" error-code="not-allowed"> - <doc> - If the alternate-exchange is not empty and if the queue already exists with a different - alternate-exchange, then the declaration MUST result in an exception. - </doc> - </exception> - - <exception name="unknown-exchange" error-code="not-found"> - <doc> - if the alternate-exchange does not match the name of any existing exchange on the - server, then an exception must be raised. - </doc> - </exception> - </field> - - <field name="passive" type="bit" label="do not create queue"> - <doc> - If set, the server will not create the queue. This field allows the client to assert the - presence of a queue without modifying the server state. - </doc> - - <exception name="passive" error-code="not-found"> - <doc> - The client MAY ask the server to assert that a queue exists without creating the queue - if not. If the queue does not exist, the server treats this as a failure. - </doc> - <doc type="scenario"> - Client declares an existing queue with the passive option and expects the command to - succeed. Client then attempts to declare a non-existent queue with the passive option, - and the server must close the session with the correct exception. - </doc> - </exception> - </field> - - <field name="durable" type="bit" label="request a durable queue"> - <doc> - If set when creating a new queue, the queue will be marked as durable. Durable queues - remain active when a server restarts. Non-durable queues (transient queues) are purged - if/when a server restarts. Note that durable queues do not necessarily hold persistent - messages, although it does not make sense to send persistent messages to a transient - queue. - </doc> - - <rule name="persistence"> - <doc> - The queue definition MUST survive the server losing all transient memory, e.g. a - machine restart. - </doc> - <doc type="scenario"> - Client creates a durable queue; server is then restarted. Client then attempts to send - message to the queue. The message should be successfully delivered. - </doc> - </rule> - - <rule name="types"> - <doc> - The server MUST support both durable and transient queues. - </doc> - <doc type="scenario"> - A client creates two named queues, one durable and one transient. - </doc> - </rule> - - <rule name="pre-existence"> - <doc> - The server MUST ignore the durable field if the queue already exists. - </doc> - <doc type="scenario"> - A client creates two named queues, one durable and one transient. The client then - attempts to declare the two queues using the same names again, but reversing the value - of the durable flag in each case. Verify that the queues still exist with the original - durable flag values. - </doc> - </rule> - </field> - - <field name="exclusive" type="bit" label="request an exclusive queue"> - <doc> - Exclusive queues can only be used from one session at a time. Once a session - declares an exclusive queue, that queue cannot be used by any other session until the - declaring session closes. - </doc> - - <rule name="types"> - <doc> - The server MUST support both exclusive (private) and non-exclusive (shared) queues. - </doc> - <doc type="scenario"> - A client creates two named queues, one exclusive and one non-exclusive. - </doc> - </rule> - - <exception name="in-use" error-code="resource-locked"> - <doc> - If the server receives a declare, bind, consume or get request for a queue that has been - declared as exclusive by an existing client session, it MUST raise an exception. - </doc> - <doc type="scenario"> - A client declares an exclusive named queue. A second client on a different session - attempts to declare a queue of the same name. - </doc> - </exception> - </field> - - <field name="auto-delete" type="bit" label="auto-delete queue when unused"> - <doc> - If this field is set and the exclusive field is also set, then the queue MUST be deleted - when the session closes. - - If this field is set and the exclusive field is not set the queue is deleted when all - the consumers have finished using it. Last consumer can be cancelled either explicitly - or because its session is closed. If there was no consumer ever on the queue, it won't - be deleted. - </doc> - - <rule name="pre-existence"> - <doc> - The server MUST ignore the auto-delete field if the queue already exists. - </doc> - <doc type="scenario"> - A client creates two named queues, one as auto-delete and one explicit-delete. The - client then attempts to declare the two queues using the same names again, but reversing - the value of the auto-delete field in each case. Verify that the queues still exist with - the original auto-delete flag values. - </doc> - </rule> - </field> - - <field name="arguments" type="map" label="arguments for declaration"> - <doc> - A set of arguments for the declaration. The syntax and semantics of these arguments - depends on the server implementation. This field is ignored if passive is 1. - </doc> - - <exception name="unknown-argument" error-code="not-implemented"> - <doc> - If the arguments field contains arguments which are not understood by the server, - it MUST raise an exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: queue.delete - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="delete" code="0x2" label="delete a queue"> - <doc> - This command deletes a queue. When a queue is deleted any pending messages are sent to the - alternate-exchange if defined, or discarded if it is not. - </doc> - - - <implement role="server" handle="MUST" /> - - <field name="queue" type="name" required="true"> - <doc> - Specifies the name of the queue to delete. - </doc> - - <exception name="empty-name" error-code="invalid-argument"> - <doc> - If the queue name in this command is empty, the server MUST raise an exception. - </doc> - </exception> - - <exception name="queue-exists" error-code="not-found"> - <doc> - The queue must exist. If the client attempts to delete a non-existing queue the server - MUST raise an exception. - </doc> - </exception> - </field> - - <field name="if-unused" type="bit" label="delete only if unused"> - <doc> - If set, the server will only delete the queue if it has no consumers. If the queue has - consumers the server does does not delete it but raises an exception instead. - </doc> - - <exception name="if-unused-flag" error-code="precondition-failed"> - <doc> - The server MUST respect the if-unused flag when deleting a queue. - </doc> - </exception> - </field> - - <field name="if-empty" type="bit" label="delete only if empty"> - <doc> - If set, the server will only delete the queue if it has no messages. - </doc> - <exception name="not-empty" error-code="precondition-failed"> - <doc> - If the queue is not empty the server MUST raise an exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: queue.purge - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="purge" code="0x3" label="purge a queue"> - <doc> - This command removes all messages from a queue. It does not cancel subscribers. Purged - messages are deleted without any formal "undo" mechanism. - </doc> - - <rule name="empty"> - <doc> - A call to purge MUST result in an empty queue. - </doc> - </rule> - - <rule name="pending-messages"> - <doc> - The server MUST NOT purge messages that have already been sent to a client but not yet - accepted. - </doc> - </rule> - - <rule name="purge-recovery"> - <doc> - The server MAY implement a purge queue or log that allows system administrators to recover - accidentally-purged messages. The server SHOULD NOT keep purged messages in the same - storage spaces as the live messages since the volumes of purged messages may get very - large. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <field name="queue" type="name" required="true"> - <doc> - Specifies the name of the queue to purge. - </doc> - - <exception name="empty-name" error-code="invalid-argument"> - <doc> - If the the queue name in this command is empty, the server MUST raise an exception. - </doc> - </exception> - - <exception name="queue-exists" error-code="not-found"> - <doc> - The queue MUST exist. Attempting to purge a non-existing queue MUST cause an exception. - </doc> - </exception> - </field> - </command> - - <!-- - Command: queue.query - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="query" code="0x4" label="request information about a queue"> - <doc> - This command requests information about a queue. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="queue" type="name" label="the queried queue" required="true"/> - - <result> - <struct name="queue-query-result" size="4" code="0x1" pack="2"> - <doc> - This is sent in response to queue.query, and conveys the requested information about a - queue. If no queue with the specified name exists then none of the fields within the - returned result struct will be populated. - </doc> - - <field name="queue" type="name" required="true"> - <doc> - Reports the name of the queue. - </doc> - </field> - - <field name="alternate-exchange" type="exchange.name" /> - - <field name="durable" type="bit" /> - - <field name="exclusive" type="bit" /> - - <field name="auto-delete" type="bit" /> - - <field name="arguments" type="map" /> - - <field name="message-count" type="uint32" label="number of messages in queue" - required="true"> - <doc> Reports the number of messages in the queue. </doc> - </field> - - <field name="subscriber-count" type="uint32" label="number of subscribers" - required="true"> - <doc> - Reports the number of subscribers for the queue. - </doc> - </field> - </struct> - </result> - </command> - - </class> - - <!-- == Class: file ========================================================================== --> - - <class name="file" code="0x9" label="work with file content"> - <doc> - The file class provides commands that support reliable file transfer. File messages have a - specific set of properties that are required for interoperability with file transfer - applications. File messages and acknowledgements are subject to session transactions. Note - that the file class does not provide message browsing commands; these are not compatible with - the staging model. Applications that need browsable file transfer should use Message content - and the Message class. - </doc> - - <doc type="grammar"> - file = C:QOS S:QOS-OK - / C:CONSUME S:CONSUME-OK - / C:CANCEL - / C:OPEN S:OPEN-OK C:STAGE content - / S:OPEN C:OPEN-OK S:STAGE content - / C:PUBLISH - / S:DELIVER - / S:RETURN - / C:ACK - / C:REJECT - </doc> - - <rule name="reliable-storage"> - <doc> - The server MUST make a best-effort to hold file messages on a reliable storage mechanism. - </doc> - </rule> - - <rule name="no-discard"> - <doc> - The server MUST NOT discard a file message in case of a queue overflow. The server MUST use - the Session.Flow command to slow or stop a file message publisher when necessary. - </doc> - </rule> - - <rule name="priority-levels"> - <doc> - The server MUST implement at least 2 priority levels for file messages, where priorities 0-4 - and 5-9 are treated as two distinct levels. The server MAY implement up to 10 priority - levels. - </doc> - </rule> - - <rule name="acknowledgement-support"> - <doc> - The server MUST support both automatic and explicit acknowledgements on file content. - </doc> - </rule> - - <role name="server" implement="MAY" /> - <role name="client" implement="MAY" /> - - <!-- These are the properties for a File content --> - <struct name="file-properties" size="4" code="0x1" pack="2"> - <field name="content-type" type="str8" label="MIME content type" /> - <field name="content-encoding" type="str8" label="MIME content encoding" /> - <field name="headers" type="map" label="message header field table" /> - <field name="priority" type="uint8" label="message priority, 0 to 9" /> - <field name="reply-to" type="str8" label="destination to reply to" /> - <field name="message-id" type="str8" label="application message identifier" /> - <field name="filename" type="str8" label="message filename" /> - <field name="timestamp" type="datetime" label="message timestamp" /> - <!-- This field is deprecated pending review --> - <field name="cluster-id" type="str8" label="intra-cluster routing identifier" /> - </struct> - - <domain name="return-code" type="uint16" label="return code from server"> - <doc> - The return code. The AMQP return codes are defined by this enum. - </doc> - <enum> - <choice name="content-too-large" value="311"> - <doc> - The client attempted to transfer content larger than the server could accept. - </doc> - </choice> - - <choice name="no-route" value="312"> - <doc> - The exchange cannot route a message, most likely due to an invalid routing key. Only - when the mandatory flag is set. - </doc> - </choice> - - <choice name="no-consumers" value="313"> - <doc> - The exchange cannot deliver to a consumer when the immediate flag is set. As a result of - pending data on the queue or the absence of any consumers of the queue. - </doc> - </choice> - </enum> - </domain> - - <!-- - Command: file.qos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="qos" code="0x1" label="specify quality of service"> - <doc> - This command requests a specific quality of service. The QoS can be specified for the - current session or for all sessions on the connection. The particular properties and - semantics of a qos command always depend on the content class semantics. Though the qos - command could in principle apply to both peers, it is currently meaningful only for the - server. - </doc> - - <implement role="server" handle="MUST" /> - - <response name="qos-ok" /> - - <field name="prefetch-size" type="uint32" label="pre-fetch window in octets"> - <doc> - The client can request that messages be sent in advance so that when the client finishes - processing a message, the following message is already held locally, rather than needing - to be sent within the session. Pre-fetching gives a performance improvement. This field - specifies the pre-fetch window size in octets. May be set to zero, meaning "no specific - limit". Note that other pre-fetch limits may still apply. The prefetch-size is ignored if - the no-ack option is set. - </doc> - </field> - - <field name="prefetch-count" type="uint16" label="pre-fetch window in messages"> - <doc> - Specifies a pre-fetch window in terms of whole messages. This is compatible with some file - API implementations. This field may be used in combination with the prefetch-size field; a - message will only be sent in advance if both pre-fetch windows (and those at the session - and connection level) allow it. The prefetch-count is ignored if the no-ack option is set. - </doc> - - <rule name="prefetch-discretion"> - <doc> - The server MAY send less data in advance than allowed by the client's specified - pre-fetch windows but it MUST NOT send more. - </doc> - </rule> - </field> - - <field name="global" type="bit" label="apply to entire connection"> - <doc> - By default the QoS settings apply to the current session only. If this field is set, they - are applied to the entire connection. - </doc> - </field> - </command> - - <!-- - Command: file.qos-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="qos-ok" code="0x2" label="confirm the requested qos"> - <doc> - This command tells the client that the requested QoS levels could be handled by the server. - The requested QoS applies to all active consumers until a new QoS is defined. - </doc> - - <implement role="client" handle="MUST" /> - </command> - - <!-- - Command: file.consume - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="consume" code="0x3" label="start a queue consumer"> - <doc> - This command asks the server to start a "consumer", which is a transient request for - messages from a specific queue. Consumers last as long as the session they were created on, - or until the client cancels them. - </doc> - - <rule name="min-consumers"> - <doc> - The server SHOULD support at least 16 consumers per queue, unless the queue was declared - as private, and ideally, impose no limit except as defined by available resources. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <response name="consume-ok" /> - - <field name="queue" type="queue.name"> - <doc> - Specifies the name of the queue to consume from. - </doc> - - <exception name="queue-exists-if-empty" error-code="not-allowed"> - <doc> - If the queue name in this command is empty, the server MUST raise an exception. - </doc> - </exception> - </field> - - <field name="consumer-tag" type="str8"> - <doc> - Specifies the identifier for the consumer. The consumer tag is local to a connection, so - two clients can use the same consumer tags. - </doc> - - <exception name="not-existing-consumer" error-code="not-allowed"> - <doc> - The tag MUST NOT refer to an existing consumer. If the client attempts to create two - consumers with the same non-empty tag the server MUST raise an exception. - </doc> - </exception> - - <exception name="not-empty-consumer-tag" error-code="not-allowed"> - <doc> - The client MUST NOT specify a tag that is empty or blank. - </doc> - <doc type="scenario"> - Attempt to create a consumers with an empty tag. - </doc> - </exception> - </field> - - <field name="no-local" type="bit"> - <doc>If the no-local field is set the server will not send messages to the connection that - published them.</doc> - </field> - - <field name="no-ack" type="bit" label="no acknowledgement needed"> - <doc> - If this field is set the server does not expect acknowledgements for messages. That is, - when a message is delivered to the client the server automatically and silently - acknowledges it on behalf of the client. This functionality increases performance but at - the cost of reliability. Messages can get lost if a client dies before it can deliver them - to the application. - </doc> - </field> - - <field name="exclusive" type="bit" label="request exclusive access"> - <doc> - Request exclusive consumer access, meaning only this consumer can access the queue. - </doc> - - <exception name="in-use" error-code="resource-locked"> - <doc> - If the server cannot grant exclusive access to the queue when asked, - because there are - other consumers active - it MUST raise an exception. - </doc> - </exception> - </field> - - <field name="nowait" type="bit" label="do not send a reply command"> - <doc> - If set, the server will not respond to the command. The client should not wait for a reply - command. If the server could not complete the command it will raise an exception. - </doc> - </field> - - <field name="arguments" type="map" label="arguments for consuming"> - <doc> - A set of arguments for the consume. The syntax and semantics of these arguments depends on - the providers implementation. - </doc> - </field> - </command> - - <command name="consume-ok" code="0x4" label="confirm a new consumer"> - <doc> - This command provides the client with a consumer tag which it MUST use in commands that work - with the consumer. - </doc> - - <implement role="client" handle="MUST" /> - - <field name="consumer-tag" type="str8"> - <doc> - Holds the consumer tag specified by the client or provided by the server. - </doc> - </field> - </command> - - <!-- - Command: file.cancel - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="cancel" code="0x5" label="end a queue consumer"> - <doc> - This command cancels a consumer. This does not affect already delivered messages, but it - does mean the server will not send any more messages for that consumer. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="consumer-tag" type="str8"> - <doc> - the identifier of the consumer to be cancelled. - </doc> - </field> - </command> - - <!-- - Command: file.open - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="open" code="0x6" label="request to start staging"> - <doc> - This command requests permission to start staging a message. Staging means sending the - message into a temporary area at the recipient end and then delivering the message by - referring to this temporary area. Staging is how the protocol handles partial file transfers - - if a message is partially staged and the connection breaks, the next time the sender - starts to stage it, it can restart from where it left off. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <response name="open-ok" /> - - <field name="identifier" type="str8" label="staging identifier"> - <doc> - This is the staging identifier. This is an arbitrary string chosen by the sender. For - staging to work correctly the sender must use the same staging identifier when staging the - same message a second time after recovery from a failure. A good choice for the staging - identifier would be the SHA1 hash of the message properties data (including the original - filename, revised time, etc.). - </doc> - </field> - - <field name="content-size" type="uint64" label="message content size"> - <doc> - The size of the content in octets. The recipient may use this information to allocate or - check available space in advance, to avoid "disk full" errors during staging of very large - messages. - </doc> - - <rule name="content-size"> - <doc> - The sender MUST accurately fill the content-size field. Zero-length content is - permitted. - </doc> - </rule> - </field> - </command> - - <!-- - Command: file.open-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="open-ok" code="0x7" label="confirm staging ready"> - <doc> - This command confirms that the recipient is ready to accept staged data. If the message was - already partially-staged at a previous time the recipient will report the number of octets - already staged. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <response name="stage" /> - - <field name="staged-size" type="uint64" label="already staged amount"> - <doc> - The amount of previously-staged content in octets. For a new message this will be zero. - </doc> - - <rule name="behavior"> - <doc> - The sender MUST start sending data from this octet offset in the message, counting from - zero. - </doc> - </rule> - - <rule name="staging"> - <doc> - The recipient MAY decide how long to hold partially-staged content and MAY implement - staging by always discarding partially-staged content. However if it uses the file - content type it MUST support the staging commands. - </doc> - </rule> - </field> - </command> - - <!-- - Command: file.stage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="stage" code="0x8" label="stage message content"> - <doc> - This command stages the message, sending the message content to the recipient from the octet - offset specified in the Open-Ok command. - </doc> - - <implement role="server" handle="MUST" /> - <implement role="client" handle="MUST" /> - - <segments> - <header required="true"> - <entry type="file-properties"/> - </header> - <body/> - </segments> - </command> - - <!-- - Command: file.publish - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="publish" code="0x9" label="publish a message"> - <doc> - This command publishes a staged file message to a specific exchange. The file message will - be routed to queues as defined by the exchange configuration and distributed to any active - consumers when the transaction, if any, is committed. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="exchange" type="exchange.name"> - <doc> - Specifies the name of the exchange to publish to. The exchange name can be empty, meaning - the default exchange. If the exchange name is specified, and that exchange does not exist, - the server will raise an exception. - </doc> - - <rule name="default"> - <doc> - The server MUST accept a blank exchange name to mean the default exchange. - </doc> - </rule> - - <exception name="refusal" error-code="not-implemented"> - <doc> - The exchange MAY refuse file content in which case it MUST send an exception. - </doc> - </exception> - </field> - - <field name="routing-key" type="str8" label="Message routing key"> - <doc> - Specifies the routing key for the message. The routing key is used for routing messages - depending on the exchange configuration. - </doc> - </field> - - <field name="mandatory" type="bit" label="indicate mandatory routing"> - <doc> - This flag tells the server how to react if the message cannot be routed to a queue. If - this flag is set, the server will return an unroutable message with a Return command. If - this flag is zero, the server silently drops the message. - </doc> - - <rule name="implementation"> - <doc> - The server SHOULD implement the mandatory flag. - </doc> - </rule> - </field> - - <field name="immediate" type="bit" label="request immediate delivery"> - <doc> - This flag tells the server how to react if the message cannot be routed to a queue - consumer immediately. If this flag is set, the server will return an undeliverable message - with a Return command. If this flag is zero, the server will queue the message, but with - no guarantee that it will ever be consumed. - </doc> - - <rule name="implementation"> - <doc> - The server SHOULD implement the immediate flag. - </doc> - </rule> - </field> - - <field name="identifier" type="str8" label="staging identifier"> - <doc> - This is the staging identifier of the message to publish. The message must have been - staged. Note that a client can send the Publish command asynchronously without waiting for - staging to finish. - </doc> - </field> - </command> - - <!-- - Command: file.return - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="return" code="0xa" label="return a failed message"> - <doc> - This command returns an undeliverable message that was published with the "immediate" flag - set, or an unroutable message published with the "mandatory" flag set. The reply code and - text provide information about the reason that the message was undeliverable. - </doc> - - <implement role="client" handle="MUST" /> - - <field name="reply-code" type="return-code" /> - - <field name="reply-text" type="str8" label="The localized reply text."> - <doc> - This text can be logged as an aid to resolving issues. - </doc> - </field> - - <field name="exchange" type="exchange.name"> - <doc> - Specifies the name of the exchange that the message was originally published to. - </doc> - </field> - - <field name="routing-key" type="str8" label="Message routing key"> - <doc> - Specifies the routing key name specified when the message was published. - </doc> - </field> - - <segments> - <header required="true"> - <entry type="file-properties"/> - </header> - <body/> - </segments> - </command> - - <!-- - Command: file.deliver - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="deliver" code="0xb" label="notify the client of a consumer message"> - <doc> - This command delivers a staged file message to the client, via a consumer. In the - asynchronous message delivery model, the client starts a consumer using the consume command, - then the server responds with Deliver commands as and when messages arrive for that - consumer. - </doc> - - <rule name="redelivery-tracking"> - <doc> - The server SHOULD track the number of times a message has been delivered to clients and - when a message is redelivered a certain number of times - e.g. 5 times - without being - acknowledged, the server SHOULD consider the message to be non-processable (possibly - causing client applications to abort), and move the message to a dead letter queue. - </doc> - </rule> - - <implement role="client" handle="MUST" /> - - <field name="consumer-tag" type="str8" /> - - <field name="delivery-tag" type="uint64" > - <doc> - The server-assigned and session-specific delivery tag - </doc> - - <rule name="non-zero"> - <doc> - The server MUST NOT use a zero value for delivery tags. Zero is reserved for client use, - meaning "all messages so far received". - </doc> - </rule> - </field> - - <field name="redelivered" type="bit" label="Indicate possible duplicate delivery"> - <doc> - This boolean flag indicates that the message may have been previously delivered to this - or another client. - </doc> - </field> - - <field name="exchange" type="exchange.name"> - <doc> - Specifies the name of the exchange that the message was originally published to. - </doc> - </field> - - <field name="routing-key" type="str8" label="Message routing key"> - <doc> - Specifies the routing key name specified when the message was published. - </doc> - </field> - - <field name="identifier" type="str8" label="staging identifier"> - <doc> - This is the staging identifier of the message to deliver. The message must have been - staged. Note that a server can send the Deliver command asynchronously without waiting for - staging to finish. - </doc> - </field> - </command> - - <!-- - Command: file.ack - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="ack" code="0xc" label="acknowledge one or more messages"> - <doc> - This command acknowledges one or more messages delivered via the Deliver command. The client - can ask to confirm a single message or a set of messages up to and including a specific - message. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="delivery-tag" type="uint64" > - <doc> - The identifier of the message being acknowledged - </doc> - <rule name="session-local"> - <doc> - The delivery tag is valid only within the session from which the message was received. - i.e. A client MUST NOT receive a message on one session and then acknowledge it on - another. - </doc> - </rule> - </field> - - <field name="multiple" type="bit" label="acknowledge multiple messages"> - <doc> - If set to 1, the delivery tag is treated as "up to and including", so that the client can - acknowledge multiple messages with a single command. If set to zero, the delivery tag - refers to a single message. If the multiple field is 1, and the delivery tag is zero, - tells the server to acknowledge all outstanding messages. - </doc> - - <rule name="validation"> - <doc> - The server MUST validate that a non-zero delivery-tag refers to an delivered message, - and raise an exception if this is not the case. - </doc> - </rule> - </field> - </command> - - <!-- - Command: file.reject - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="reject" code="0xd" label="reject an incoming message"> - <doc> - This command allows a client to reject a message. It can be used to return untreatable - messages to their original queue. Note that file content is staged before delivery, so the - client will not use this command to interrupt delivery of a large message. - </doc> - - <rule name="server-interpretation"> - <doc> - The server SHOULD interpret this command as meaning that the client is unable to process - the message at this time. - </doc> - </rule> - - <rule name="not-selection"> - <doc> - A client MUST NOT use this command as a means of selecting messages to process. A rejected - message MAY be discarded or dead-lettered, not necessarily passed to another client. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <field name="delivery-tag" type="uint64"> - <doc> - the identifier of the message to be rejected - </doc> - <rule name="session-local"> - <doc> - The delivery tag is valid only within the session from which the message was received. - i.e. A client MUST NOT receive a message on one session and then reject it on another. - </doc> - </rule> - </field> - - <field name="requeue" type="bit" label="requeue the message"> - <doc> - If this field is zero, the message will be discarded. If this bit is 1, the server will - attempt to requeue the message. - </doc> - - <rule name="requeue-strategy"> - <doc> - The server MUST NOT deliver the message to the same client within the context of the - current session. The recommended strategy is to attempt to deliver the message to an - alternative consumer, and if that is not possible, to move the message to a dead-letter - queue. The server MAY use more sophisticated tracking to hold the message on the queue - and redeliver it to the same client at a later stage. - </doc> - </rule> - </field> - </command> - - </class> - - <!-- == Class: stream ======================================================================== --> - - <class name="stream" code="0xa" label="work with streaming content"> - <doc> - The stream class provides commands that support multimedia streaming. The stream class uses - the following semantics: one message is one packet of data; delivery is unacknowledged and - unreliable; the consumer can specify quality of service parameters that the server can try to - adhere to; lower-priority messages may be discarded in favor of high priority messages. - </doc> - - <doc type="grammar"> - stream = C:QOS S:QOS-OK - / C:CONSUME S:CONSUME-OK - / C:CANCEL - / C:PUBLISH content - / S:RETURN - / S:DELIVER content - </doc> - - <rule name="overflow-discard"> - <doc> - The server SHOULD discard stream messages on a priority basis if the queue size exceeds some - configured limit. - </doc> - </rule> - - <rule name="priority-levels"> - <doc> - The server MUST implement at least 2 priority levels for stream messages, where priorities - 0-4 and 5-9 are treated as two distinct levels. The server MAY implement up to 10 priority - levels. - </doc> - </rule> - - <rule name="acknowledgement-support"> - <doc> - The server MUST implement automatic acknowledgements on stream content. That is, as soon as - a message is delivered to a client via a Deliver command, the server must remove it from the - queue. - </doc> - </rule> - - <role name="server" implement="MAY" /> - <role name="client" implement="MAY" /> - - <!-- These are the properties for a Stream content --> - <struct name="stream-properties" size="4" code="0x1" pack="2"> - <field name="content-type" type="str8" label="MIME content type" /> - <field name="content-encoding" type="str8" label="MIME content encoding" /> - <field name="headers" type="map" label="message header field table" /> - <field name="priority" type="uint8" label="message priority, 0 to 9" /> - <field name="timestamp" type="datetime" label="message timestamp" /> - </struct> - - <domain name="return-code" type="uint16" label="return code from server"> - <doc> - The return code. The AMQP return codes are defined by this enum. - </doc> - <enum> - <choice name="content-too-large" value="311"> - <doc> - The client attempted to transfer content larger than the server could accept. - </doc> - </choice> - - <choice name="no-route" value="312"> - <doc> - The exchange cannot route a message, most likely due to an invalid routing key. Only - when the mandatory flag is set. - </doc> - </choice> - - <choice name="no-consumers" value="313"> - <doc> - The exchange cannot deliver to a consumer when the immediate flag is set. As a result of - pending data on the queue or the absence of any consumers of the queue. - </doc> - </choice> - </enum> - </domain> - - <!-- - Command: stream.qos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="qos" code="0x1" label="specify quality of service"> - <doc> - This command requests a specific quality of service. The QoS can be specified for the - current session or for all sessions on the connection. The particular properties and - semantics of a qos command always depend on the content class semantics. Though the qos - command could in principle apply to both peers, it is currently meaningful only for the - server. - </doc> - - <implement role="server" handle="MUST" /> - - <response name="qos-ok" /> - - <field name="prefetch-size" type="uint32" label="pre-fetch window in octets"> - <doc> - The client can request that messages be sent in advance so that when the client finishes - processing a message, the following message is already held locally, rather than needing - to be sent within the session. Pre-fetching gives a performance improvement. This field - specifies the pre-fetch window size in octets. May be set to zero, meaning "no specific - limit". Note that other pre-fetch limits may still apply. - </doc> - </field> - - <field name="prefetch-count" type="uint16" label="pre-fetch window in messages"> - <doc> - Specifies a pre-fetch window in terms of whole messages. This field may be used in - combination with the prefetch-size field; a message will only be sent in advance if both - pre-fetch windows (and those at the session and connection level) allow it. - </doc> - </field> - - <field name="consume-rate" type="uint32" label="transfer rate in octets/second"> - <doc> - Specifies a desired transfer rate in octets per second. This is usually determined by the - application that uses the streaming data. A value of zero means "no limit", i.e. as - rapidly as possible. - </doc> - - <rule name="ignore-prefetch"> - <doc> - The server MAY ignore the pre-fetch values and consume rates, depending on the type of - stream and the ability of the server to queue and/or reply it. - </doc> - </rule> - - <rule name="drop-by-priority"> - <doc> - The server MAY drop low-priority messages in favor of high-priority messages. - </doc> - </rule> - </field> - - <field name="global" type="bit" label="apply to entire connection"> - <doc> - By default the QoS settings apply to the current session only. If this field is set, they - are applied to the entire connection. - </doc> - </field> - </command> - - <!-- - Command: stream.qos-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="qos-ok" code="0x2" label="confirm the requested qos"> - <doc> - This command tells the client that the requested QoS levels could be handled by the server. - The requested QoS applies to all active consumers until a new QoS is defined. - </doc> - - <implement role="client" handle="MUST" /> - </command> - - <!-- - Command: stream.consume - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="consume" code="0x3" label="start a queue consumer"> - <doc> - This command asks the server to start a "consumer", which is a transient request for - messages from a specific queue. Consumers last as long as the session they were created on, - or until the client cancels them. - </doc> - - <rule name="min-consumers"> - <doc> - The server SHOULD support at least 16 consumers per queue, unless the queue was declared - as private, and ideally, impose no limit except as defined by available resources. - </doc> - </rule> - - <rule name="priority-based-delivery"> - <doc> - Streaming applications SHOULD use different sessions to select different streaming - resolutions. AMQP makes no provision for filtering and/or transforming streams except on - the basis of priority-based selective delivery of individual messages. - </doc> - </rule> - - <implement role="server" handle="MUST" /> - - <response name="consume-ok" /> - - <field name="queue" type="queue.name"> - <doc> - Specifies the name of the queue to consume from. - </doc> - - <exception name="queue-exists-if-empty" error-code="not-allowed"> - <doc> - If the queue name in this command is empty, the server MUST raise an exception. - </doc> - </exception> - </field> - - <field name="consumer-tag" type="str8"> - <doc> - Specifies the identifier for the consumer. The consumer tag is local to a connection, so - two clients can use the same consumer tags. - </doc> - - <exception name="not-existing-consumer" error-code="not-allowed"> - <doc> - The tag MUST NOT refer to an existing consumer. If the client attempts to create two - consumers with the same non-empty tag the server MUST raise an exception. - </doc> - </exception> - - <exception name="not-empty-consumer-tag" error-code="not-allowed"> - <doc> - The client MUST NOT specify a tag that is empty or blank. - </doc> - <doc type="scenario"> - Attempt to create a consumers with an empty tag. - </doc> - </exception> - </field> - - <field name="no-local" type="bit"> - <doc>If the no-local field is set the server will not send messages to the connection that - published them.</doc> - </field> - - <field name="exclusive" type="bit" label="request exclusive access"> - <doc> - Request exclusive consumer access, meaning only this consumer can access the queue. - </doc> - - <exception name="in-use" error-code="resource-locked"> - <doc> - If the server cannot grant exclusive access to the queue when asked, - because there are - other consumers active - it MUST raise an exception with return code 405 - (resource locked). - </doc> - </exception> - </field> - - <field name="nowait" type="bit" label="do not send a reply command"> - <doc> - If set, the server will not respond to the command. The client should not wait for a reply - command. If the server could not complete the command it will raise an exception. - </doc> - </field> - - <field name="arguments" type="map" label="arguments for consuming"> - <doc> - A set of arguments for the consume. The syntax and semantics of these arguments depends on - the providers implementation. - </doc> - </field> - </command> - - <!-- - Command: stream.consume-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="consume-ok" code="0x4" label="confirm a new consumer"> - <doc> - This command provides the client with a consumer tag which it may use in commands that work - with the consumer. - </doc> - - <implement role="client" handle="MUST" /> - - <field name="consumer-tag" type="str8"> - <doc> - Holds the consumer tag specified by the client or provided by the server. - </doc> - </field> - </command> - - <!-- - Command: stream.cancel - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="cancel" code="0x5" label="end a queue consumer"> - <doc> - This command cancels a consumer. Since message delivery is asynchronous the client may - continue to receive messages for a short while after cancelling a consumer. It may process - or discard these as appropriate. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="consumer-tag" type="str8" /> - </command> - - <!-- - Command: stream.publish - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="publish" code="0x6" label="publish a message"> - <doc> - This command publishes a message to a specific exchange. The message will be routed to - queues as defined by the exchange configuration and distributed to any active consumers as - appropriate. - </doc> - - <implement role="server" handle="MUST" /> - - <field name="exchange" type="exchange.name"> - <doc> - Specifies the name of the exchange to publish to. The exchange name can be empty, meaning - the default exchange. If the exchange name is specified, and that exchange does not exist, - the server will raise an exception. - </doc> - - <rule name="default"> - <doc> - The server MUST accept a blank exchange name to mean the default exchange. - </doc> - </rule> - - <exception name="refusal" error-code="not-implemented"> - <doc> - The exchange MAY refuse stream content in which case it MUST respond with an exception. - </doc> - </exception> - </field> - - <field name="routing-key" type="str8" label="Message routing key"> - <doc> - Specifies the routing key for the message. The routing key is used for routing messages - depending on the exchange configuration. - </doc> - </field> - - <field name="mandatory" type="bit" label="indicate mandatory routing"> - <doc> - This flag tells the server how to react if the message cannot be routed to a queue. If - this flag is set, the server will return an unroutable message with a Return command. If - this flag is zero, the server silently drops the message. - </doc> - - <rule name="implementation"> - <doc> - The server SHOULD implement the mandatory flag. - </doc> - </rule> - </field> - - <field name="immediate" type="bit" label="request immediate delivery"> - <doc> - This flag tells the server how to react if the message cannot be routed to a queue - consumer immediately. If this flag is set, the server will return an undeliverable message - with a Return command. If this flag is zero, the server will queue the message, but with - no guarantee that it will ever be consumed. - </doc> - - <rule name="implementation"> - <doc> - The server SHOULD implement the immediate flag. - </doc> - </rule> - </field> - - <segments> - <header required="true"> - <entry type="stream-properties"/> - </header> - <body/> - </segments> - </command> - - <!-- - Command: stream.return - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="return" code="0x7" label="return a failed message"> - <doc> - This command returns an undeliverable message that was published with the "immediate" flag - set, or an unroutable message published with the "mandatory" flag set. The reply code and - text provide information about the reason that the message was undeliverable. - </doc> - - <implement role="client" handle="MUST" /> - - <field name="reply-code" type="return-code" /> - - <field name="reply-text" type="str8" label="The localized reply text."> - <doc> - The localized reply text. This text can be logged as an aid to resolving issues. - </doc> - </field> - - <field name="exchange" type="exchange.name"> - <doc> - Specifies the name of the exchange that the message was originally published to. - </doc> - </field> - - <field name="routing-key" type="str8" label="Message routing key"> - <doc> - Specifies the routing key name specified when the message was published. - </doc> - </field> - - <segments> - <header required="true"> - <entry type="stream-properties"/> - </header> - <body/> - </segments> - </command> - - <!-- - Command: stream.deliver - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - - <command name="deliver" code="0x8" label="notify the client of a consumer message"> - <doc> - This command delivers a message to the client, via a consumer. In the asynchronous message - delivery model, the client starts a consumer using the Consume command, then the server - responds with Deliver commands as and when messages arrive for that consumer. - </doc> - - <implement role="client" handle="MUST" /> - - <field name="consumer-tag" type="str8" /> - - <field name="delivery-tag" type="uint64"> - <doc> - The server-assigned and session-specific delivery tag - </doc> - <rule name="session-local"> - <doc> - The delivery tag is valid only within the session from which the message was received. - i.e. A client MUST NOT receive a message on one session and then acknowledge it on - another. - </doc> - </rule> - </field> - - <field name="exchange" type="exchange.name"> - <doc> - Specifies the name of the exchange that the message was originally published to. - </doc> - </field> - - <field name="queue" type="queue.name" required="true"> - <doc> - Specifies the name of the queue that the message came from. Note that a single session can - start many consumers on different queues. - </doc> - </field> - - <segments> - <header required="true"> - <entry type="stream-properties"/> - </header> - <body/> - </segments> - </command> - - </class> - -</amqp> diff --git a/ruby/lib/qpid/specs/amqp.0-10.dtd b/ruby/lib/qpid/specs/amqp.0-10.dtd deleted file mode 100644 index 2be198525a..0000000000 --- a/ruby/lib/qpid/specs/amqp.0-10.dtd +++ /dev/null @@ -1,246 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - Copyright Notice - ================ - (c) Copyright Cisco Systems, Credit Suisse, Deutsche Börse Systems, Envoy Technologies, Inc., - Goldman Sachs, IONA Technologies PLC, iMatix Corporation sprl.,JPMorgan Chase Bank Inc. N.A, - Novell, Rabbit Technologies Ltd., Red Hat, Inc., TWIST Process Innovations ltd, and 29West Inc - 2006, 2007. All rights reserved. - - License - ======= - JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc., iMatix Corporation, IONA - Technologies, Red Hat, Inc., TWIST Process Innovations, and 29West Inc. (collectively, the - "Authors") each hereby grants to you a worldwide, perpetual, royalty-free, nontransferable, - nonexclusive license to (i) copy, display, distribute and implement the Advanced Messaging Queue - Protocol ("AMQP") Specification and (ii) the Licensed Claims that are held by the Authors, all for - the purpose of implementing the Advanced Messaging Queue Protocol Specification. Your license and - any rights under this Agreement will terminate immediately without notice from any Author if you - bring any claim, suit, demand, or action related to the Advanced Messaging Queue Protocol - Specification against any Author. Upon termination, you shall destroy all copies of the Advanced - Messaging Queue Protocol Specification in your possession or control. - - As used hereunder, "Licensed Claims" means those claims of a patent or patent application, - throughout the world, excluding design patents and design registrations, owned or controlled, or - that can be sublicensed without fee and in compliance with the requirements of this Agreement, by - an Author or its affiliates now or at any future time and which would necessarily be infringed by - implementation of the Advanced Messaging Queue Protocol Specification. A claim is necessarily - infringed hereunder only when it is not possible to avoid infringing it because there is no - plausible non-infringing alternative for implementing the required portions of the Advanced - Messaging Queue Protocol Specification. Notwithstanding the foregoing, Licensed Claims shall not - include any claims other than as set forth above even if contained in the same patent as Licensed - Claims; or that read solely on any implementations of any portion of the Advanced Messaging Queue - Protocol Specification that are not required by the Advanced Messaging Queue Protocol - Specification, or that, if licensed, would require a payment of royalties by the licensor to - unaffiliated third parties. Moreover, Licensed Claims shall not include (i) any enabling - technologies that may be necessary to make or use any Licensed Product but are not themselves - expressly set forth in the Advanced Messaging Queue Protocol Specification (e.g., semiconductor - manufacturing technology, compiler technology, object oriented technology, networking technology, - operating system technology, and the like); or (ii) the implementation of other published - standards developed elsewhere and merely referred to in the body of the Advanced Messaging Queue - Protocol Specification, or (iii) any Licensed Product and any combinations thereof the purpose or - function of which is not required for compliance with the Advanced Messaging Queue Protocol - Specification. For purposes of this definition, the Advanced Messaging Queue Protocol - Specification shall be deemed to include both architectural and interconnection requirements - essential for interoperability and may also include supporting source code artifacts where such - architectural, interconnection requirements and source code artifacts are expressly identified as - being required or documentation to achieve compliance with the Advanced Messaging Queue Protocol - Specification. - - As used hereunder, "Licensed Products" means only those specific portions of products (hardware, - software or combinations thereof) that implement and are compliant with all relevant portions of - the Advanced Messaging Queue Protocol Specification. - - The following disclaimers, which you hereby also acknowledge as to any use you may make of the - Advanced Messaging Queue Protocol Specification: - - THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS," AND THE AUTHORS MAKE NO - REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS - OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE - IMPLEMENTATION OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD - PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. - - THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL - DAMAGES ARISING OUT OF OR RELATING TO ANY USE, IMPLEMENTATION OR OF THE ADVANCED - MESSAGING QUEUE PROTOCOL SPECIFICATION. - - The name and trademarks of the Authors may NOT be used in any manner, including advertising or - publicity pertaining to the Advanced Messaging Queue Protocol Specification or its contents - without specific, written prior permission. Title to copyright in the Advanced Messaging Queue - Protocol Specification will at all times remain with the Authors. - - No other rights are granted by implication, estoppel or otherwise. - - Upon termination of your license or rights under this Agreement, you shall destroy all copies of - the Advanced Messaging Queue Protocol Specification in your possession or control. - - Trademarks - ========== - "JPMorgan", "JPMorgan Chase", "Chase", the JPMorgan Chase logo and the Octagon Symbol are - trademarks of JPMorgan Chase & Co. - - IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl. - - IONA, IONA Technologies, and the IONA logos are trademarks of IONA Technologies PLC and/or its - subsidiaries. - - LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered trademarks of Red Hat, - Inc. in the US and other countries. - - Java, all Java-based trademarks and OpenOffice.org are trademarks of Sun Microsystems, Inc. in the - United States, other countries, or both. - - Other company, product, or service names may be trademarks or service marks of others. - - Links to full AMQP specification: - ================================= - http://www.envoytech.org/spec/amq/ - http://www.iona.com/opensource/amqp/ - http://www.redhat.com/solutions/specifications/amqp/ - http://www.twiststandards.org/tiki-index.php?page=AMQ - http://www.imatix.com/amqp ---> - -<!ELEMENT amqp (doc|type|struct|domain|constant|class)*> -<!ATTLIST amqp - xmlns CDATA #IMPLIED - major CDATA #REQUIRED - minor CDATA #REQUIRED - port CDATA #REQUIRED - comment CDATA #IMPLIED -> - -<!ELEMENT constant (doc|rule)*> -<!ATTLIST constant - name CDATA #REQUIRED - value CDATA #REQUIRED - label CDATA #IMPLIED -> - -<!ELEMENT type (doc|rule)*> -<!ATTLIST type - name CDATA #REQUIRED - label CDATA #IMPLIED - code CDATA #IMPLIED - fixed-width CDATA #IMPLIED - variable-width CDATA #IMPLIED -> - -<!ELEMENT domain (doc|rule|enum)*> -<!ATTLIST domain - name CDATA #REQUIRED - type CDATA #IMPLIED - label CDATA #IMPLIED -> - -<!ELEMENT struct (field|doc|rule)*> -<!ATTLIST struct - name CDATA #REQUIRED - label CDATA #IMPLIED - size (0|1|2|4) #IMPLIED - pack (0|1|2|4) #IMPLIED - code CDATA #IMPLIED> - -<!ELEMENT enum (choice)*> - -<!ELEMENT choice (doc|rule)*> -<!ATTLIST choice - name CDATA #REQUIRED - value CDATA #REQUIRED -> - -<!ELEMENT class (doc|role|rule|struct|domain|control|command)*> -<!ATTLIST class - name CDATA #REQUIRED - code CDATA #REQUIRED - label CDATA #IMPLIED -> - -<!ELEMENT role (doc|rule)*> -<!ATTLIST role - name CDATA #REQUIRED - implement (MAY|SHOULD|MUST) #REQUIRED -> - -<!ELEMENT control (doc|implement|rule|field|response)*> -<!ATTLIST control - name CDATA #REQUIRED - code CDATA #REQUIRED - label CDATA #IMPLIED -> - -<!ELEMENT command ((doc|implement|rule|exception|field|response)*, result?, segments?)> -<!ATTLIST command - name CDATA #REQUIRED - code CDATA #REQUIRED - label CDATA #IMPLIED -> - -<!ELEMENT implement (doc|rule)*> -<!ATTLIST implement - role CDATA #REQUIRED - handle (MAY|SHOULD|MUST) #REQUIRED - send (MAY|SHOULD|MUST) #IMPLIED -> - -<!ELEMENT field (doc|rule|exception)*> -<!ATTLIST field - name CDATA #REQUIRED - type CDATA #IMPLIED - default CDATA #IMPLIED - code CDATA #IMPLIED - label CDATA #IMPLIED - required CDATA #IMPLIED -> - -<!ELEMENT rule (doc*)> -<!ATTLIST rule - name CDATA #REQUIRED - label CDATA #IMPLIED -> - -<!ELEMENT exception (doc*)> -<!ATTLIST exception - name CDATA #REQUIRED - error-code CDATA #IMPLIED - label CDATA #IMPLIED -> - -<!ELEMENT response (doc|rule)*> -<!ATTLIST response - name CDATA #IMPLIED -> - -<!ELEMENT result (doc|rule|struct)*> -<!ATTLIST result - type CDATA #IMPLIED -> - -<!ELEMENT segments (doc|rule|header|body)*> - -<!ELEMENT header (doc|rule|entry)*> -<!ATTLIST header - required (true|false) #IMPLIED -> - -<!ELEMENT entry (doc|rule)*> -<!ATTLIST entry - type CDATA #REQUIRED -> - -<!ELEMENT body (doc|rule)*> -<!ATTLIST body - required (true|false) #IMPLIED -> - -<!ELEMENT doc (#PCDATA|xref)*> -<!ATTLIST doc - type (grammar|scenario|picture|bnf|todo) #IMPLIED - title CDATA #IMPLIED -> - -<!ELEMENT xref (#PCDATA)> -<!ATTLIST xref - ref CDATA #REQUIRED> diff --git a/ruby/lib/qpid/test.rb b/ruby/lib/qpid/test.rb deleted file mode 100644 index 2e643f4348..0000000000 --- a/ruby/lib/qpid/test.rb +++ /dev/null @@ -1,35 +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/spec08" -require "qpid/client" - -module Qpid08 - - module Test - - def connect() - spec = Spec.load("../specs/amqp.0-8.xml") - c = Client.new("0.0.0.0", 5672, spec) - c.start({"LOGIN" => "guest", "PASSWORD" => "guest"}) - return c - end - - end - -end diff --git a/ruby/lib/qpid/traverse.rb b/ruby/lib/qpid/traverse.rb deleted file mode 100644 index 67358a7eb1..0000000000 --- a/ruby/lib/qpid/traverse.rb +++ /dev/null @@ -1,64 +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 Object - - public - - def traverse() - traverse! {|o| yield(o); o} - end - - def traverse_children!() - instance_variables.each {|v| - value = instance_variable_get(v) - replacement = yield(value) - instance_variable_set(v, replacement) unless replacement.equal? value - } - end - - def traverse!(replacements = {}) - return replacements[__id__] if replacements.has_key? __id__ - replacement = yield(self) - replacements[__id__] = replacement - traverse_children! {|o| o.traverse!(replacements) {|c| yield(c)}} - return replacement - end - -end - -class Array - def traverse_children!() - map! {|o| yield(o)} - end -end - -class Hash - def traverse_children!() - mods = {} - each_pair {|k, v| - key = yield(k) - value = yield(v) - mods[key] = value unless key.equal? k and value.equal? v - delete(k) unless key.equal? k - } - - merge!(mods) - end -end diff --git a/ruby/lib/qpid/util.rb b/ruby/lib/qpid/util.rb deleted file mode 100644 index 2dbc37da09..0000000000 --- a/ruby/lib/qpid/util.rb +++ /dev/null @@ -1,75 +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 'thread' -require 'monitor' - -# Monkeypatch -class MonitorMixin::ConditionVariable - - # Wait until BLOCK returns TRUE or TIMEOUT seconds have passed - # Return TRUE if BLOCK returned TRUE within the TIMEOUT, FALSE - # otherswise - def wait_for(timeout=nil, &block) - start = Time.now - passed = 0 - until yield - if timeout.nil? - wait - elsif passed < timeout - wait(timeout) - else - return false - end - passed = Time.now - start - end - return true - end -end - -module Qpid::Util - - # Similar to Python's threading.Event - class Event - def initialize - @monitor = Monitor.new - @cond = @monitor.new_cond - @set = false - end - - def set - @monitor.synchronize do - @set = true - @cond.signal - end - end - - def clear - @monitor.synchronize { @set = false } - end - - def wait(timeout = nil) - @monitor.synchronize do - unless @set - @cond.wait_for(timeout) { @set } - end - end - end - end -end |