summaryrefslogtreecommitdiff
path: root/ruby/lib
diff options
context:
space:
mode:
Diffstat (limited to 'ruby/lib')
-rw-r--r--ruby/lib/qpid.rb41
-rw-r--r--ruby/lib/qpid/assembler.rb148
-rw-r--r--ruby/lib/qpid/client.rb136
-rw-r--r--ruby/lib/qpid/codec.rb457
-rw-r--r--ruby/lib/qpid/codec08.rb265
-rw-r--r--ruby/lib/qpid/config.rb32
-rw-r--r--ruby/lib/qpid/connection.rb222
-rw-r--r--ruby/lib/qpid/connection08.rb252
-rw-r--r--ruby/lib/qpid/datatypes.rb353
-rw-r--r--ruby/lib/qpid/delegates.rb237
-rw-r--r--ruby/lib/qpid/fields.rb49
-rw-r--r--ruby/lib/qpid/framer.rb212
-rw-r--r--ruby/lib/qpid/invoker.rb65
-rw-r--r--ruby/lib/qpid/packer.rb33
-rw-r--r--ruby/lib/qpid/peer.rb289
-rw-r--r--ruby/lib/qpid/qmf.rb1957
-rw-r--r--ruby/lib/qpid/queue.rb101
-rw-r--r--ruby/lib/qpid/session.rb458
-rw-r--r--ruby/lib/qpid/spec.rb183
-rw-r--r--ruby/lib/qpid/spec010.rb485
-rw-r--r--ruby/lib/qpid/spec08.rb190
-rw-r--r--ruby/lib/qpid/specs/amqp.0-10-qpid-errata.xml6654
-rw-r--r--ruby/lib/qpid/specs/amqp.0-10.dtd246
-rw-r--r--ruby/lib/qpid/test.rb35
-rw-r--r--ruby/lib/qpid/traverse.rb64
-rw-r--r--ruby/lib/qpid/util.rb75
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