stomp 1.1.10 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +17 -0
- data/README.rdoc +11 -3
- data/Rakefile +2 -2
- data/bin/catstomp +3 -3
- data/examples/client11_ex1.rb +78 -0
- data/examples/client11_putget1.rb +57 -0
- data/examples/conn11_ex1.rb +101 -0
- data/examples/conn11_ex2.rb +75 -0
- data/examples/conn11_hb1.rb +46 -0
- data/examples/consumer.rb +2 -0
- data/examples/get11conn_ex1.rb +107 -0
- data/examples/get11conn_ex2.rb +67 -0
- data/examples/logexamp.rb +18 -2
- data/examples/publisher.rb +2 -0
- data/examples/put11conn_ex1.rb +43 -0
- data/examples/putget11_rh1.rb +81 -0
- data/examples/slogger.rb +79 -1
- data/examples/stomp11_common.rb +45 -0
- data/examples/topic_consumer.rb +19 -0
- data/examples/topic_publisher.rb +15 -0
- data/lib/stomp.rb +4 -0
- data/lib/stomp/client.rb +36 -4
- data/lib/stomp/codec.rb +41 -0
- data/lib/stomp/connection.rb +623 -29
- data/lib/stomp/constants.rb +78 -0
- data/lib/stomp/errors.rb +60 -2
- data/lib/stomp/ext/hash.rb +3 -1
- data/lib/stomp/message.rb +32 -3
- data/lib/stomp/version.rb +4 -2
- data/spec/client_shared_examples.rb +2 -0
- data/spec/client_spec.rb +2 -0
- data/spec/connection_spec.rb +30 -9
- data/spec/message_spec.rb +2 -0
- data/spec/spec_helper.rb +2 -0
- data/stomp.gemspec +25 -24
- data/test/test_client.rb +152 -44
- data/test/test_codec.rb +83 -0
- data/test/test_connection.rb +138 -25
- data/test/test_connection1p.rb +251 -0
- data/test/test_helper.rb +48 -0
- data/test/test_message.rb +69 -19
- data/test/tlogger.rb +155 -0
- metadata +52 -69
data/examples/consumer.rb
CHANGED
@@ -0,0 +1,107 @@
|
|
1
|
+
#
|
2
|
+
# The current require dance for different Ruby versions.
|
3
|
+
# Change this to suit your requirements.
|
4
|
+
#
|
5
|
+
if Kernel.respond_to?(:require_relative)
|
6
|
+
require_relative("./stomp11_common")
|
7
|
+
else
|
8
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
9
|
+
require "stomp11_common"
|
10
|
+
end
|
11
|
+
include Stomp11Common
|
12
|
+
#
|
13
|
+
# Stomp 1.1 Receive Example 1
|
14
|
+
# ===========================
|
15
|
+
#
|
16
|
+
# Purpose: to demonstrate receiving messages using Stomp 1.1.
|
17
|
+
#
|
18
|
+
conn = get_connection() # Use helper method to obtain a Stomp#connection
|
19
|
+
raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
|
20
|
+
#
|
21
|
+
# To start receiving messages, you must first subscribe. This is similar
|
22
|
+
# to using Stomp 1.0.
|
23
|
+
#
|
24
|
+
# However, with Stomp 1.1:
|
25
|
+
#
|
26
|
+
# * for subscribe, the 'id' header is now _required_
|
27
|
+
# * for unsubscribe, the 'id' header is now _required_
|
28
|
+
#
|
29
|
+
# The 'id' header specifies a 'subscription id' that must be unique for
|
30
|
+
# the current session.
|
31
|
+
#
|
32
|
+
qname = "/queue/nodea.nodeb.nodec"
|
33
|
+
#
|
34
|
+
# Here is an example of allowed functionality in 1.0 that is not allowed in 1.1:
|
35
|
+
#
|
36
|
+
begin
|
37
|
+
conn.subscribe qname
|
38
|
+
rescue RuntimeError => sre
|
39
|
+
puts "Rescue: #{sre}, #{sre.message}"
|
40
|
+
end
|
41
|
+
#
|
42
|
+
# So, you must specify an 'id' header. And it must be unique within the
|
43
|
+
# current session.
|
44
|
+
#
|
45
|
+
# You can build your own unique ids of course. That is a valid option.
|
46
|
+
# In order to provide you with some assistance in generating unique ids,
|
47
|
+
# two convenience methods are provided with the connection:
|
48
|
+
#
|
49
|
+
# * sha1 - generate a sha1 hash of some data you supply. This may be sufficient for many purposes.
|
50
|
+
# * uuid - generate a type 4 UUID. This would be sufficient in all cases.
|
51
|
+
#
|
52
|
+
# Get a sha1:
|
53
|
+
#
|
54
|
+
sha1 = conn.sha1(qname) # sha1 of the queue name perhaps
|
55
|
+
puts "Queue name: #{qname}, sha1: #{sha1}"
|
56
|
+
#
|
57
|
+
# Or perhaps a different sha1:
|
58
|
+
#
|
59
|
+
tn = Time.now.to_f.to_s # Maybe unique itself.
|
60
|
+
sha1 = conn.sha1(tn)
|
61
|
+
puts "Time now: #{tn}, sha1: #{sha1}"
|
62
|
+
#
|
63
|
+
# Or a Type 4 UUID:
|
64
|
+
#
|
65
|
+
uuid = conn.uuid()
|
66
|
+
puts "Type 4 UUID: #{uuid}"
|
67
|
+
#
|
68
|
+
# You can specify the 'id' in the subscribe call in one of two ways:
|
69
|
+
#
|
70
|
+
# a) In the headers parameter
|
71
|
+
# b) In the third positional parameter, the subId
|
72
|
+
#
|
73
|
+
# So, using the 'uuid', either:
|
74
|
+
#
|
75
|
+
# a) conn.subscribe qname, {'id' => uuid}
|
76
|
+
# b) conn.subscribe qname, {}, uuid
|
77
|
+
#
|
78
|
+
conn.subscribe qname, {'id' => uuid} # First style
|
79
|
+
#
|
80
|
+
# Within a session, you may not subscribe to the same subscription id.
|
81
|
+
#
|
82
|
+
begin
|
83
|
+
conn.subscribe qname, {'id' => uuid} # Second time
|
84
|
+
rescue RuntimeError => sre
|
85
|
+
puts "Rescue: #{sre}, #{sre.message}"
|
86
|
+
end
|
87
|
+
#
|
88
|
+
# Once you have subscribed, you may receive as usual
|
89
|
+
#
|
90
|
+
1.upto(nmsgs()) do
|
91
|
+
received = conn.receive
|
92
|
+
puts "Received data: #{received.body}"
|
93
|
+
end
|
94
|
+
#
|
95
|
+
# For unsubscribe, you must use the 'id' you used on subscribe.
|
96
|
+
#
|
97
|
+
# You have the same options for placing this id in the headers or in the 3rd
|
98
|
+
# positional parameter.
|
99
|
+
#
|
100
|
+
conn.unsubscribe qname, {}, uuid # Second style
|
101
|
+
#
|
102
|
+
# And finally, disconnect.
|
103
|
+
#
|
104
|
+
conn.disconnect
|
105
|
+
|
106
|
+
|
107
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# The current require dance for different Ruby versions.
|
3
|
+
# Change this to suit your requirements.
|
4
|
+
#
|
5
|
+
if Kernel.respond_to?(:require_relative)
|
6
|
+
require_relative("./stomp11_common")
|
7
|
+
else
|
8
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
9
|
+
require "stomp11_common"
|
10
|
+
end
|
11
|
+
include Stomp11Common
|
12
|
+
#
|
13
|
+
# Stomp 1.1 Receive Example 2
|
14
|
+
# ===========================
|
15
|
+
#
|
16
|
+
# Purpose: to demonstrate receiving messages using Stomp 1.1, and using
|
17
|
+
# 'ack => client'.
|
18
|
+
#
|
19
|
+
conn = get_connection() # Use helper method to obtain a Stomp#connection
|
20
|
+
raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
|
21
|
+
#
|
22
|
+
qname = "/queue/nodea.nodeb.nodec"
|
23
|
+
#
|
24
|
+
uuid = conn.uuid()
|
25
|
+
puts "Subscribe id: #{uuid}"
|
26
|
+
#
|
27
|
+
# Subscribe with client ack mode
|
28
|
+
#
|
29
|
+
conn.subscribe qname, {'id' => uuid, 'ack' => 'client'} #
|
30
|
+
#
|
31
|
+
# Once you have subscribed, you may receive as usual
|
32
|
+
#
|
33
|
+
1.upto(nmsgs()) do
|
34
|
+
received = conn.receive
|
35
|
+
puts "Received data: #{received.body}"
|
36
|
+
#
|
37
|
+
# We want now to ACK this message. In Stomp 1.0, a 'message-id' header was
|
38
|
+
# required for the ACK. In Stomp 1.1, and additional header is required:
|
39
|
+
#
|
40
|
+
# * 'subscription' => id
|
41
|
+
#
|
42
|
+
msgid = received.headers['message-id']
|
43
|
+
#
|
44
|
+
# What you cannot do:
|
45
|
+
#
|
46
|
+
begin
|
47
|
+
conn.ack msgid
|
48
|
+
rescue RuntimeError => sre
|
49
|
+
puts "Rescue: #{sre}, #{sre.message}"
|
50
|
+
end
|
51
|
+
#
|
52
|
+
# Try a valid 1.1 ACK
|
53
|
+
#
|
54
|
+
conn.ack msgid, {'subscription' => uuid}
|
55
|
+
puts "ACK - msgid: #{msgid}, subscription: #{uuid}"
|
56
|
+
end
|
57
|
+
#
|
58
|
+
# Unsubscribe
|
59
|
+
#
|
60
|
+
conn.unsubscribe qname, {}, uuid # Second style
|
61
|
+
#
|
62
|
+
# And finally, disconnect.
|
63
|
+
#
|
64
|
+
conn.disconnect
|
65
|
+
|
66
|
+
|
67
|
+
|
data/examples/logexamp.rb
CHANGED
@@ -14,10 +14,11 @@ llog.debug "LE Starting"
|
|
14
14
|
|
15
15
|
# //////////////////////////////////////////////////////////////////////////////
|
16
16
|
mylog = Slogger::new # The client provided STOMP callback logger
|
17
|
+
# -*- encoding: utf-8 -*-
|
17
18
|
|
18
19
|
# //////////////////////////////////////////////////////////////////////////////
|
19
20
|
user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest'
|
20
|
-
password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : '
|
21
|
+
password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest'
|
21
22
|
host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost'
|
22
23
|
port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61613
|
23
24
|
# //////////////////////////////////////////////////////////////////////////////
|
@@ -33,6 +34,7 @@ hash = { :hosts => [
|
|
33
34
|
|
34
35
|
# //////////////////////////////////////////////////////////////////////////////
|
35
36
|
# For a Connection:
|
37
|
+
llog.debug "LE Connection processing starts"
|
36
38
|
conn = Stomp::Connection.new(hash)
|
37
39
|
conn.disconnect
|
38
40
|
# //////////////////////////////////////////////////////////////////////////////
|
@@ -40,10 +42,24 @@ llog.debug "LE Connection processing complete"
|
|
40
42
|
|
41
43
|
# //////////////////////////////////////////////////////////////////////////////
|
42
44
|
# For a Client:
|
45
|
+
llog.debug "LE Client processing starts"
|
43
46
|
conn = Stomp::Client.new(hash)
|
44
47
|
conn.close
|
45
48
|
# //////////////////////////////////////////////////////////////////////////////
|
46
|
-
|
49
|
+
llog.debug "LE Client processing complete"
|
50
|
+
|
51
|
+
# //////////////////////////////////////////////////////////////////////////////
|
52
|
+
# For a Connection with other calls:
|
53
|
+
llog.debug "LE Connection Enhanced processing starts"
|
54
|
+
conn = Stomp::Connection.new(hash)
|
55
|
+
#
|
56
|
+
dest = "/queue/loggerq1"
|
57
|
+
conn.publish dest, "a logger message"
|
58
|
+
conn.subscribe dest
|
59
|
+
msg = conn.receive
|
60
|
+
conn.disconnect
|
61
|
+
# //////////////////////////////////////////////////////////////////////////////
|
62
|
+
llog.debug "LE Connection Enhanced processing complete"
|
47
63
|
|
48
64
|
# //////////////////////////////////////////////////////////////////////////////
|
49
65
|
llog.debug "LE Ending"
|
data/examples/publisher.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# The current require dance for different Ruby versions.
|
3
|
+
# Change this to suit your requirements.
|
4
|
+
#
|
5
|
+
if Kernel.respond_to?(:require_relative)
|
6
|
+
require_relative("./stomp11_common")
|
7
|
+
else
|
8
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
9
|
+
require "stomp11_common"
|
10
|
+
end
|
11
|
+
include Stomp11Common
|
12
|
+
#
|
13
|
+
# Stomp 1.1 Publish Example
|
14
|
+
# =========================
|
15
|
+
#
|
16
|
+
# Purpose: to demonstrate sending messages using Stomp 1.1.
|
17
|
+
#
|
18
|
+
conn = get_connection() # Use helper method to obtain a Stomp#connection
|
19
|
+
raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
|
20
|
+
#
|
21
|
+
# Publishing simple data is as it was with Stomp 1.0.
|
22
|
+
#
|
23
|
+
# Note: Stomp 1.1 brokers seem to prefer using '.' as delimiters in queue
|
24
|
+
# name spaces. Hence, the queue name used here.
|
25
|
+
#
|
26
|
+
qname = "/queue/nodea.nodeb.nodec"
|
27
|
+
data = "message payload"
|
28
|
+
headers = {}
|
29
|
+
#
|
30
|
+
# The 'data' and 'headers' may be omitted, as with Stomp 1.0
|
31
|
+
#
|
32
|
+
1.upto(nmsgs()) do |i|
|
33
|
+
msg = "#{data}: #{i}"
|
34
|
+
conn.publish qname, msg , headers
|
35
|
+
puts "Sent data: #{msg}"
|
36
|
+
end
|
37
|
+
#
|
38
|
+
# And finally, disconnect.
|
39
|
+
#
|
40
|
+
conn.disconnect
|
41
|
+
|
42
|
+
|
43
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# The current require dance for different Ruby versions.
|
3
|
+
# Change this to suit your requirements.
|
4
|
+
#
|
5
|
+
if Kernel.respond_to?(:require_relative)
|
6
|
+
require_relative("./stomp11_common")
|
7
|
+
else
|
8
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
9
|
+
require "stomp11_common"
|
10
|
+
end
|
11
|
+
include Stomp11Common
|
12
|
+
#
|
13
|
+
# Stomp 1.1 Send/Receive Example - Repeated Headers
|
14
|
+
# =================================================
|
15
|
+
#
|
16
|
+
# Purpose: to demonstrate sending and receiving using Stomp 1.1, and an unusual
|
17
|
+
# aspect of the specification. What is demonstrated here is the use of
|
18
|
+
# 'repeated headers'. Note that brokers MAY support repeated headers as
|
19
|
+
# demonstrated, but are not required to provide this support. This example
|
20
|
+
# should run against the Apollo broker. It will *not* currently run against
|
21
|
+
# RabbitMQ. YMMV depending on your broker.
|
22
|
+
#
|
23
|
+
# See: http://stomp.github.com/stomp-specification-1.1.html#Repeated_Header_Entries
|
24
|
+
#
|
25
|
+
conn = get_connection() # Use helper method to obtain a Stomp#connection
|
26
|
+
raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
|
27
|
+
#
|
28
|
+
# The gem supports repeated headers by allowing the 'value' part of a header
|
29
|
+
# to be an Array instance.
|
30
|
+
#
|
31
|
+
# On 'publish', all values in the Array are placed on the wire and sent to the
|
32
|
+
# broker in order.
|
33
|
+
#
|
34
|
+
# On 'receive', if repeated headers are detected, an Array instance is created
|
35
|
+
# to hold the repeated values. This is presented the the calling client to
|
36
|
+
# be processed per client requirements.
|
37
|
+
#
|
38
|
+
qname = "/queue/nodea.nodeb.nodec"
|
39
|
+
data = "message payload: #{Time.now.to_f}"
|
40
|
+
key2_repeats = ["key2val3", "key2val2", "key2val1" ]
|
41
|
+
headers = {"key1" => "value1", # A normal header
|
42
|
+
"key2" => key2_repeats, # A repeated header
|
43
|
+
"key3" => "value3", # Another normal header
|
44
|
+
}
|
45
|
+
#
|
46
|
+
# Ship it.
|
47
|
+
#
|
48
|
+
conn.publish qname, data , headers
|
49
|
+
puts "Sent data: #{data}"
|
50
|
+
#
|
51
|
+
# Receive phase.
|
52
|
+
#
|
53
|
+
uuid = conn.uuid()
|
54
|
+
conn.subscribe qname, {"id" => uuid}
|
55
|
+
received = conn.receive
|
56
|
+
conn.unsubscribe qname, {"id" => uuid}
|
57
|
+
#
|
58
|
+
# Check that we received what we sent.
|
59
|
+
#
|
60
|
+
raise "Unexpected payload" unless data == received.body
|
61
|
+
raise "Missing key" unless received.headers["key2"]
|
62
|
+
raise "Repeats not present" unless received.headers.has_value?(key2_repeats)
|
63
|
+
raise "Unexpected repeat values" unless key2_repeats == received.headers["key2"]
|
64
|
+
#
|
65
|
+
# Demonstrate how to process repeated headers received by display of those
|
66
|
+
# received headers for a visual check.
|
67
|
+
#
|
68
|
+
received.headers.each_pair do |k,v|
|
69
|
+
if v.is_a?(Array)
|
70
|
+
v.each do |e|
|
71
|
+
puts "#{k}:#{e}"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
puts "#{k}:#{v}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
#
|
78
|
+
# And finally, disconnect.
|
79
|
+
#
|
80
|
+
conn.disconnect
|
81
|
+
|
data/examples/slogger.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
=begin
|
2
4
|
|
3
5
|
Example STOMP call back logger class.
|
@@ -11,6 +13,10 @@ Optional callback methods:
|
|
11
13
|
|
12
14
|
on_miscerr: on miscellaneous xmit/recv errors
|
13
15
|
|
16
|
+
on_publish: publish called
|
17
|
+
on_subscribe: subscribe called
|
18
|
+
on_receive: receive called and successful
|
19
|
+
|
14
20
|
All methods are optional, at the user's requirements.
|
15
21
|
|
16
22
|
If a method is not provided, it is not called (of course.)
|
@@ -69,7 +75,6 @@ class Slogger
|
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
72
|
-
|
73
78
|
# Log miscellaneous errors
|
74
79
|
def on_miscerr(parms, errstr)
|
75
80
|
begin
|
@@ -80,6 +85,79 @@ class Slogger
|
|
80
85
|
end
|
81
86
|
end
|
82
87
|
|
88
|
+
# Subscribe
|
89
|
+
def on_subscribe(parms, headers)
|
90
|
+
begin
|
91
|
+
@log.debug "Subscribe Parms #{info(parms)}"
|
92
|
+
@log.debug "Subscribe Headers #{headers}"
|
93
|
+
rescue
|
94
|
+
@log.debug "Subscribe oops"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Publish
|
99
|
+
def on_publish(parms, message, headers)
|
100
|
+
begin
|
101
|
+
@log.debug "Publish Parms #{info(parms)}"
|
102
|
+
@log.debug "Publish Message #{message}"
|
103
|
+
@log.debug "Publish Headers #{headers}"
|
104
|
+
rescue
|
105
|
+
@log.debug "Publish oops"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Receive
|
110
|
+
def on_receive(parms, result)
|
111
|
+
begin
|
112
|
+
@log.debug "Receive Parms #{info(parms)}"
|
113
|
+
@log.debug "Receive Result #{result}"
|
114
|
+
rescue
|
115
|
+
@log.debug "Receive oops"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Stomp 1.1+ - heart beat read (receive) failed
|
120
|
+
def on_hbread_fail(parms, ticker_data)
|
121
|
+
begin
|
122
|
+
@log.debug "Hbreadf Parms #{info(parms)}"
|
123
|
+
@log.debug "Hbreadf Result #{ticker_data}"
|
124
|
+
rescue
|
125
|
+
@log.debug "Hbreadf oops"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Stomp 1.1+ - heart beat send (transmit) failed
|
130
|
+
def on_hbwrite_fail(parms, ticker_data)
|
131
|
+
begin
|
132
|
+
@log.debug "Hbwritef Parms #{info(parms)}"
|
133
|
+
@log.debug "Hbwritef Result #{ticker_data}"
|
134
|
+
rescue
|
135
|
+
@log.debug "Hbwritef oops"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# Stomp 1.1+ - heart beat read (receive) failed
|
141
|
+
def on_hbread_fail(parms, ticker_data)
|
142
|
+
begin
|
143
|
+
@log.debug "Hbreadf Parms #{info(parms)}"
|
144
|
+
@log.debug "Hbreadf Result #{ticker_data}"
|
145
|
+
rescue
|
146
|
+
@log.debug "Hbreadf oops"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Stomp 1.1+ - heart beat thread fires
|
151
|
+
def on_hbfire(parms, type, time)
|
152
|
+
begin
|
153
|
+
@log.debug "HBfire #{type} " + "=" * 30
|
154
|
+
@log.debug "HBfire #{type} Parms #{info(parms)}"
|
155
|
+
@log.debug "HBfire #{type} Time #{time}"
|
156
|
+
rescue
|
157
|
+
@log.debug "HBfire #{type} oops"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
83
161
|
private
|
84
162
|
|
85
163
|
def info(parms)
|