thechrisoshow-smqueue 0.1.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/lib/smqueue.rb ADDED
@@ -0,0 +1,227 @@
1
+ # SMQueue
2
+ # Simple Message Queue client
3
+ # Sean O'Halpin, 2008
4
+
5
+ # The high-level client API is:
6
+ # - SMQueue(config).put msg, headers = {}
7
+ # - msg = SMQueue(config).get(headers = {})
8
+ # - SMQueue(config).get(headers = {}) do |msg|
9
+ # end
10
+ # todo - [X] add :durable option (and fail if no client_id specified)
11
+ # todo - [ ] gemify - use Mr Bones
12
+ # todo - [ ] change to class (so can be subclassed) - so you're working with an SMQueue instance
13
+ # todo - [ ] write protocol (open, close, put, get) in SMQueue (so don't have to do it in adaptors)
14
+ # todo - [ ] simplify StompAdapter (get rid of sent_messages stuff)
15
+ # todo - [ ] simplify adapter interface
16
+ # todo - [ ] sort out libpath
17
+
18
+ require 'rubygems'
19
+ require 'doodle'
20
+ require 'doodle/version'
21
+ require 'yaml'
22
+
23
+ class Doodle
24
+ if Doodle::VERSION::STRING < '0.1.9'
25
+ def to_hash
26
+ doodle.attributes.inject({}) {|hash, (name, attribute)| hash[name] = send(name); hash}
27
+ end
28
+ class DoodleAttribute
29
+ has :doc, :kind => String
30
+ end
31
+ end
32
+ end
33
+
34
+ #class SMQueue < Doodle
35
+ module SMQueue
36
+
37
+ # Mr Bones project skeleton boilerplate
38
+ # :stopdoc:
39
+ VERSION = '0.1.0'
40
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
41
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
42
+ # :startdoc:
43
+
44
+ # Returns the version string for the library.
45
+ #
46
+ def self.version
47
+ VERSION
48
+ end
49
+
50
+ # Returns the library path for the module. If any arguments are given,
51
+ # they will be joined to the end of the libray path using
52
+ # <tt>File.join</tt>.
53
+ #
54
+ def self.libpath( *args )
55
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
56
+ end
57
+
58
+ # Returns the lpath for the module. If any arguments are given,
59
+ # they will be joined to the end of the path using
60
+ # <tt>File.join</tt>.
61
+ #
62
+ def self.path( *args )
63
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
64
+ end
65
+
66
+ # Utility method used to rquire all files ending in .rb that lie in the
67
+ # directory below this file that has the same name as the filename passed
68
+ # in. Optionally, a specific _directory_ name can be passed in such that
69
+ # the _filename_ does not have to be equivalent to the directory.
70
+ #
71
+ def self.require_all_libs_relative_to( fname, dir = nil )
72
+ dir ||= ::File.basename(fname, '.*')
73
+ search_me = ::File.expand_path(
74
+ ::File.join(::File.dirname(fname), dir, '*', '*.rb'))
75
+
76
+ Dir.glob(search_me).sort.each {|rb| require rb}
77
+ end
78
+
79
+ # end Bones boilerplate
80
+
81
+ class << self
82
+ def dbg(*args, &block)
83
+ if $DEBUG
84
+ if args.size > 0
85
+ STDERR.print "SMQUEUE.DBG: "
86
+ STDERR.puts(*args)
87
+ end
88
+ if block_given?
89
+ STDERR.print "SMQUEUE.DBG: "
90
+ STDERR.puts(block.call)
91
+ end
92
+ end
93
+ end
94
+
95
+ # JMS expiry time in milliseconds from now
96
+ def calc_expiry_time(seconds = 86400 * 7) # one week
97
+ ((Time.now.utc + seconds).to_f * 1000).to_i
98
+ end
99
+
100
+ # resolve a string representing a classname
101
+ def const_resolve(constant)
102
+ constant.to_s.split(/::/).reject{|x| x.empty?}.inject(self) { |prev, this| prev.const_get(this) }
103
+ end
104
+ end
105
+
106
+ class AdapterConfiguration < Doodle
107
+ has :logger, :default => nil
108
+
109
+ # need to use custom to_yaml because YAML won't serialize classes
110
+ def to_hash
111
+ doodle.attributes.inject({}) {|hash, (name, attribute)| hash[name] = send(name); hash}
112
+ end
113
+ def to_yaml(*opts)
114
+ to_hash.to_yaml(*opts)
115
+ end
116
+ def initialize(*args, &block)
117
+ #p [self.class, :initialize, args, caller]
118
+ super
119
+ end
120
+ has :adapter_class, :kind => Class do
121
+ from String, Symbol do |s|
122
+ SMQueue.const_resolve(s.to_s)
123
+ end
124
+ # Note: use closure so this is not evaluated until after NullAdapter class has been defined
125
+ default { NullAdapter }
126
+ end
127
+ has :configuration_class, :kind => Class do
128
+ init { adapter_class::Configuration }
129
+ from String do |s|
130
+ #Doodle::Utils.const_resolve(s)
131
+ SMQueue.const_resolve(s.to_s)
132
+ end
133
+ end
134
+ end
135
+
136
+ class Adapter < Doodle
137
+ has :configuration, :kind => AdapterConfiguration, :abstract => true do
138
+ from Hash do |h|
139
+ #p [:Adapter, :configuration_from_hash]
140
+ Doodle.context.last.class::Configuration.new(h)
141
+ end
142
+ from Object do |h|
143
+ #p [:Adapter, :configuration_from_object, h.inspect, h.class]
144
+ h
145
+ end
146
+ end
147
+ # these are not called anywhere...
148
+ def open(*args, &block)
149
+ end
150
+ def close(*args, &block)
151
+ end
152
+ # these are the core methods
153
+ def get(*args, &block)
154
+ end
155
+ def put(*args, &block)
156
+ end
157
+ def self.create(configuration)
158
+ # FIXME: dup config, otherwise can use it only once - prob. better way to do this
159
+ configuration = configuration.dup
160
+ adapter = configuration.delete(:adapter)
161
+ #p [:adapter, adapter]
162
+ ac = AdapterConfiguration.new(:adapter_class => adapter)
163
+ #p [:ac, ac]
164
+ klass = ac.adapter_class
165
+ #p [:class, klass]
166
+ #puts [:configuration, configuration].pretty_inspect
167
+ # klass.new(:configuration => configuration)
168
+ klass.new(:configuration => configuration)
169
+ end
170
+ end
171
+
172
+ class NullAdapter < Adapter
173
+ class Configuration < AdapterConfiguration
174
+ end
175
+ end
176
+
177
+ class Message < Doodle
178
+ has :headers, :default => { }
179
+ has :body
180
+ end
181
+
182
+ class << self
183
+ def new(*args, &block)
184
+ a = args.first
185
+ if a.kind_of?(Hash) && a.key?(:configuration)
186
+ args = [a[:configuration]]
187
+ end
188
+ Adapter.create(*args, &block)
189
+ end
190
+ end
191
+
192
+ end
193
+ def SMQueue(*args, &block)
194
+ SMQueue.new(*args, &block)
195
+ end
196
+
197
+ # SMQueue.require_all_libs_relative_to(__FILE__)
198
+
199
+ # require adapters relative to invocation path first, then from lib
200
+ [$0, __FILE__].each do |path|
201
+ base_path = File.expand_path(File.dirname(path))
202
+ adapter_path = File.join(base_path, 'smqueue', 'adapters', '*.rb')
203
+ Dir[adapter_path].each do |file|
204
+ require file
205
+ end
206
+ end
207
+
208
+ if __FILE__ == $0
209
+ yaml = %[
210
+ :adapter: :StompAdapter
211
+ :host: localhost
212
+ :port: 61613
213
+ :name: /topic/smput.test
214
+ :reliable: true
215
+ :reconnect_delay: 5
216
+ :subscription_name: test_stomp
217
+ :client_id: hello_from_stomp_adapter
218
+ :durable: false
219
+ ]
220
+
221
+ adapter = SMQueue(:configuration => YAML.load(yaml))
222
+ adapter.get do |msg|
223
+ puts msg.body
224
+ end
225
+
226
+ end
227
+
@@ -0,0 +1,101 @@
1
+ require 'spread'
2
+
3
+ module SMQueue
4
+ class SpreadAdapter < Adapter
5
+ class Configuration < AdapterConfiguration
6
+ has :channel do
7
+ rx_hostname = /[a-z_\.]+/
8
+ rx_ip = /\d+(\.\d+){3}/ # dotted quad
9
+ must 'be a name in the form "port", "port@hostname", or "port@ip"' do |s|
10
+ s =~ /\d+(@(#{rx_hostname})|(#{rx_ip}))?/
11
+ end
12
+ end
13
+ has :group do
14
+ doc "a group name or array of group names"
15
+ must "be either a String group name or an array of group names" do |s|
16
+ s.kind_of?(String) || (s.kind_of?(Array) && s.all?{ |x| x.kind_of?(String)})
17
+ end
18
+ end
19
+ has :private_name, :default => '' do
20
+ doc <<EDOC
21
+ private_name is the name of this connection. It must be unique among
22
+ all the connections to a given Spread daemon. If not specified, Spread
23
+ will assign a randomly-generated unique private name.
24
+ EDOC
25
+ end
26
+ has :all_messages, :default => false do
27
+ doc <<EDOC
28
+ all_messages indicates whether this connection should receive all
29
+ Spread messages, or just data messages.
30
+ EDOC
31
+ end
32
+ has :service_type, :default => Spread::AGREED_MESS do
33
+ service_type_map = {
34
+ :unreliable => Spread::UNRELIABLE_MESS,
35
+ :reliable => Spread::RELIABLE_MESS,
36
+ :fifo => Spread::FIFO_MESS,
37
+ :causal => Spread::CAUSAL_MESS,
38
+ :agreed => Spread::AGREED_MESS,
39
+ :safe => Spread::SAFE_MESS,
40
+ :regular => Spread::REGULAR_MESS,
41
+ }
42
+ from Symbol, String do |s|
43
+ s = s.to_s.to_sym
44
+ if service_type_map.key?(s)
45
+ service_type_map[s]
46
+ else
47
+ raise Doodle::ConversionError, "Did not recognize service_type #{s.inspect} - should be one of #{service_type_map.keys.inspect}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ has :connection, :default => nil
54
+ has :connected, :default => false
55
+
56
+ def connect
57
+ @connection = Spread::Connection.new(configuration.channel, configuration.private_name, configuration.all_messages )
58
+ connection.join(configuration.group)
59
+ configuration.private_name = connection.private_group
60
+ connected true
61
+ end
62
+ def disconnect
63
+ connection.leave group
64
+ connection.disconnect
65
+ connected false
66
+ end
67
+ def get(&block)
68
+ m = nil
69
+ connect if !connected
70
+ loop do
71
+ msg = connection.receive
72
+ if msg.data?
73
+ m = SMQueue::Message.new(
74
+ :headers => {
75
+ :private_name => configuration.private_name,
76
+ :sender => msg.sender,
77
+ :type => msg.msg_type,
78
+ :groups => msg.groups,
79
+ :reliable => msg.reliable?,
80
+ :safe => msg.safe?,
81
+ :agreed => msg.agreed?,
82
+ :causal => msg.causal?,
83
+ :fifo => msg.fifo?,
84
+ },
85
+ :body => msg.message
86
+ )
87
+ if block_given?
88
+ yield(m)
89
+ else
90
+ break
91
+ end
92
+ end
93
+ end
94
+ m
95
+ end
96
+ def put(msg)
97
+ connect if !connected
98
+ connection.multicast(msg, configuration.group, configuration.service_type, msg_type = 0, self_discard = true)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,59 @@
1
+ # set of utility adapters for SMQueue
2
+
3
+ module SMQueue
4
+ class StdioLineAdapter < Adapter
5
+ doc "reads STDIN input, creates new Message for each line of input"
6
+ class Configuration < AdapterConfiguration
7
+ end
8
+ def put(*args, &block)
9
+ STDOUT.puts *args
10
+ end
11
+ def get(*args, &block)
12
+ while input = STDIN.gets
13
+ msg = SMQueue::Message.new(:body => input)
14
+ if block_given?
15
+ yield(msg)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ module SMQueue
23
+ class StdioAdapter < StdioLineAdapter
24
+ doc "reads complete STDIN input, creates one shot Message with :body => input"
25
+ def get(*args, &block)
26
+ input = STDIN.read
27
+ msg = SMQueue::Message.new(:body => input)
28
+ if block_given?
29
+ yield(msg)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ module SMQueue
36
+ class YamlAdapter < StdioAdapter
37
+ doc "outputs message as YAML"
38
+ def put(*args)
39
+ STDOUT.puts args.to_yaml
40
+ end
41
+ end
42
+ end
43
+
44
+ require 'readline'
45
+ module SMQueue
46
+ class ReadlineAdapter < StdioLineAdapter
47
+ doc "uses readline to read input from prompt, creates new Message for each line of input"
48
+ has :prompt, :default => "> "
49
+ has :history, :default => true
50
+ def get(*args, &block)
51
+ while input = Readline.readline(prompt, history)
52
+ msg = Message.new(:body => input)
53
+ if block_given?
54
+ yield(msg)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,291 @@
1
+ require 'rstomp'
2
+ module SMQueue
3
+ class StompAdapter < Adapter
4
+ class Configuration < AdapterConfiguration
5
+ has :host, :kind => String, :default => ""
6
+ has :port, :kind => Integer, :default => 61613 # Stomp
7
+ has :secondary_host, :kind => String, :default => ""
8
+ has :secondary_port, :kind => Integer, :default => 61613
9
+ has :name, :kind => String, :default => ""
10
+ has :user, :kind => String, :default => ""
11
+ has :password, :kind => String, :default => ""
12
+ has :reliable, :default => false
13
+ has :persistent, :default => true
14
+ has :reconnect_delay, :default => 5
15
+ has :client_id, :default => nil, :kind => String
16
+ has :logfile, :default => STDERR
17
+ has :logger, :default => nil
18
+ has :subscription_name, :default => nil
19
+ has :home, :default => File.dirname(File.expand_path(__FILE__))
20
+ has :single_delivery, :default => false do
21
+ doc <<-EDOC
22
+ Note: we can't guarantee single delivery - only best effort.
23
+ Use this when receiving a message more than once is very
24
+ costly. However, be aware that you ~will~ sometimes receive
25
+ the same message more than once (so it's your responsibility
26
+ to make sure that you guard against any consequences).
27
+ EDOC
28
+ end
29
+ has :seen_messages_file do
30
+ init { File.join(home, "seen_messages.#{subscription_name}.#{client_id}.yml") }
31
+ end
32
+ has :expires, :default => 86400 * 7 do
33
+ doc <<-EDOC
34
+ Time to live in milliseconds, i.e. a relative offset not an
35
+ absolute time (as it would be in JMS).
36
+
37
+ The default time to live is one week.
38
+ EDOC
39
+ end
40
+ # to get a durable subscription, you must specify
41
+ # :durable => true
42
+ # and a :client_id (and optionally a :subscription_name)
43
+ has :durable, :default => false do
44
+ must "be true or false" do |b|
45
+ [true, false].include?(b)
46
+ end
47
+ doc <<-EDOC
48
+ Specify whether you want a durable or non-durable subscription.
49
+
50
+ Note: durable queues are ~not~ the default as this could be
51
+ v. expensive in disk usage when used thoughtlessly.
52
+ EDOC
53
+ end
54
+ must "specify client_id if durable is true" do
55
+ #pp [:durable_test, client_id, durable, !client_id.to_s.strip.empty?]
56
+ !(client_id.to_s.strip.empty? and durable)
57
+ end
58
+ end
59
+ has :connection, :default => nil
60
+
61
+ # seen_messages is used to skip over messages that have already
62
+ # been seen - only activated when :single_delivery is specified
63
+ has :seen_messages, :init => []
64
+ has :seen_message_count, :init => 10
65
+ has :seen_messages_file do
66
+ init { configuration.seen_messages_file }
67
+ end
68
+
69
+ def initialize(*args, &block)
70
+ super
71
+ restore_remembered_messages
72
+ SMQueue.dbg { [:seen_messages, seen_messages].inspect }
73
+ end
74
+
75
+ # handle an error
76
+ def handle_error(exception_class, error_message, caller)
77
+ #configuration.logger.warn error_message
78
+ raise exception_class, error_message, caller
79
+ end
80
+
81
+ # connect to message broker
82
+ def connect(*args, &block)
83
+ self.connection = RStomp::Connection.open(configuration.to_hash)
84
+ # If the connection has swapped hosts, then swap out primary and secondary
85
+ if connection.current_host != configuration.host
86
+ configuration.secondary_host = configuration.host
87
+ configuration.host = connection.current_host
88
+ end
89
+
90
+ # If the connection has swapped ports, then swap out primary and secondary
91
+ if connection.current_port != configuration.port
92
+ configuration.secondary_port = configuration.port
93
+ configuration.port = connection.current_port
94
+ end
95
+ end
96
+
97
+ # normalize hash keys (top level only)
98
+ # - normalizes keys to strings by default
99
+ # - optionally pass in name of method to use (e.g. :to_sym) to normalize keys
100
+ def normalize_keys(hash, method = :to_s)
101
+ hash = hash.dup
102
+ hash.keys.each do |k|
103
+ normalized_key = k.respond_to?(method) ? k.send(method) : k
104
+ hash[normalized_key] = hash.delete(k)
105
+ end
106
+ hash
107
+ end
108
+
109
+ # true if the message with this message_id has already been seen
110
+ def message_seen?(message_id)
111
+ self.seen_messages.include?(message_id)
112
+ end
113
+
114
+ # remember the message_id
115
+ def message_seen(message_id)
116
+ message_id = message_id.to_s.strip
117
+ if message_id != ""
118
+ self.seen_messages << message_id
119
+ SMQueue.dbg { [:smqueue, :ack, :message_seen, message_id].inspect }
120
+ if self.seen_messages.size > self.seen_message_count
121
+ self.seen_messages.shift
122
+ end
123
+ store_remembered_messages
124
+ else
125
+ SMQueue.dbg { [:smqueue, :ack, :message_seen, message_id].inspect }
126
+ end
127
+ end
128
+
129
+ # store the remembered message ids in a yaml file
130
+ def store_remembered_messages
131
+ if configuration.single_delivery
132
+ File.open(seen_messages_file, 'w') do |file|
133
+ file.write seen_messages.to_yaml
134
+ end
135
+ end
136
+ end
137
+
138
+ # reload remembered message ids from a yaml file
139
+ def restore_remembered_messages
140
+ if configuration.single_delivery
141
+ yaml = default_yaml = "--- []"
142
+ begin
143
+ File.open(seen_messages_file, 'r') do |file|
144
+ yaml = file.read
145
+ end
146
+ rescue Object
147
+ yaml = default_yaml
148
+ end
149
+ buffer = []
150
+ begin
151
+ buffer = YAML.load(yaml)
152
+ if !buffer.kind_of?(Array) or !buffer.all?{ |x| x.kind_of?(String)}
153
+ raise Exception, "Invalid seen_messages.yml file"
154
+ end
155
+ rescue Object
156
+ buffer = []
157
+ end
158
+ self.seen_messages = buffer
159
+ end
160
+ end
161
+
162
+ # acknowledge message (if headers["ack"] == "client")
163
+ def ack(subscription_headers, message)
164
+ #p [:ack, message.headers["message-id"]]
165
+ if message.headers["message-id"].to_s.strip != "" && subscription_headers["ack"].to_s == "client"
166
+ SMQueue.dbg { [:smqueue, :ack, :message, message].inspect }
167
+ connection.ack message.headers["message-id"], { }
168
+ else
169
+ SMQueue.dbg { [:smqueue, :ack, :not_acknowledging, message].inspect }
170
+ end
171
+ if ENV['PAUSE_SMQUEUE']
172
+ $stderr.print "press enter to continue> "
173
+ $stderr.flush
174
+ $stdin.gets
175
+ end
176
+ end
177
+
178
+ # get message from queue
179
+ # - if block supplied, loop forever and yield(message) for each
180
+ # message received
181
+ # default headers are:
182
+ # :ack => "client"
183
+ # :client_id => configuration.client_id
184
+ # :subscription_name => configuration.subscription_name
185
+ #
186
+ def get(headers = {}, &block)
187
+ self.connect
188
+ SMQueue.dbg { [:smqueue, :get, headers].inspect }
189
+ subscription_headers = {"ack" => "client", "activemq.prefetchSize" => 1 }
190
+ if client_id = configuration.client_id
191
+ subscription_headers["client_id"] = client_id
192
+ end
193
+ if sub_name = configuration.subscription_name
194
+ subscription_headers["subscription_name"] = sub_name
195
+ end
196
+ # if a client_id is supplied, then user wants a durable subscription
197
+ # N.B. client_id must be unique for broker
198
+ subscription_headers.update(headers)
199
+ #p [:subscription_headers_before, subscription_headers]
200
+ subscription_headers = normalize_keys(subscription_headers)
201
+ if configuration.durable and client_id = configuration.client_id || subscription_headers["client_id"]
202
+ subscription_name = configuration.subscription_name || subscription_headers["subscription_name"] || client_id
203
+ # activemq only
204
+ subscription_headers["activemq.subscriptionName"] = subscription_name
205
+ # JMS
206
+ subscription_headers["durable-subscriber-name"] = subscription_name
207
+ end
208
+ #p [:subscription_headers_after, subscription_headers]
209
+
210
+ destination = configuration.name
211
+ SMQueue.dbg { [:smqueue, :get, :subscribing, destination, :subscription_headers, subscription_headers].inspect }
212
+ connection.subscribe destination, subscription_headers
213
+ message = nil
214
+ SMQueue.dbg { [:smqueue, :get, :subscription_headers, subscription_headers].inspect }
215
+ begin
216
+ # TODO: refactor this
217
+ if block_given?
218
+ SMQueue.dbg { [:smqueue, :get, :block_given].inspect }
219
+ # todo: change to @running - (and set to false from exception handler)
220
+ # also should check to see if anything left to receive on connection before bailing out
221
+ while true
222
+ SMQueue.dbg { [:smqueue, :get, :receive].inspect }
223
+ # block until message ready
224
+ message = connection.receive
225
+ SMQueue.dbg { [:smqueue, :get, :received, message].inspect }
226
+ case message.command
227
+ when "ERROR"
228
+ SMQueue.dbg { [:smqueue, :get, :ERROR, message].inspect }
229
+ when "RECEIPT"
230
+ SMQueue.dbg { [:smqueue, :get, :RECEIPT, message].inspect }
231
+ else
232
+ SMQueue.dbg { [:smqueue, :get, :yielding].inspect }
233
+ if !message_seen?(message.headers["message-id"])
234
+ yield(message)
235
+ end
236
+ SMQueue.dbg { [:smqueue, :get, :message_seen, message.headers["message-id"]].inspect }
237
+ message_seen message.headers["message-id"]
238
+ SMQueue.dbg { [:smqueue, :get, :returned_from_yield_now_calling_ack].inspect }
239
+ ack(subscription_headers, message)
240
+ SMQueue.dbg { [:smqueue, :get, :returned_from_ack].inspect }
241
+ end
242
+ end
243
+ else
244
+ SMQueue.dbg { [:smqueue, :get, :single_shot].inspect }
245
+ message = connection.receive
246
+ SMQueue.dbg { [:smqueue, :get, :received, message].inspect }
247
+ if !(message.command == "ERROR" or message.command == "RECEIPT")
248
+ SMQueue.dbg { [:smqueue, :get, :message_seen, message.headers["message-id"]].inspect }
249
+ message_seen message.headers["message-id"]
250
+ SMQueue.dbg { [:smqueue, :get, :ack, message].inspect }
251
+ ack(subscription_headers, message)
252
+ SMQueue.dbg { [:smqueue, :get, :returned_from_ack].inspect }
253
+ end
254
+ end
255
+ rescue Object => e
256
+ SMQueue.dbg { [:smqueue, :get, :exception, e].inspect }
257
+ handle_error e, "Exception in SMQueue#get: #{e.message}", caller
258
+ ensure
259
+ SMQueue.dbg { [:smqueue, :get, :ensure].inspect }
260
+ SMQueue.dbg { [:smqueue, :unsubscribe, destination, subscription_headers].inspect }
261
+ connection.unsubscribe destination, subscription_headers
262
+ SMQueue.dbg { [:smqueue, :disconnect].inspect }
263
+ connection.disconnect
264
+ end
265
+ SMQueue.dbg { [:smqueue, :get, :return].inspect }
266
+ message
267
+ end
268
+
269
+ # put a message on the queue
270
+ # default headers are:
271
+ # :persistent => true
272
+ # :ack => "auto"
273
+ # :expires => configuration.expires
274
+ def put(body, headers = { })
275
+ SMQueue.dbg { [:smqueue, :put, body, headers].inspect }
276
+ begin
277
+ self.connect
278
+ headers = {:persistent => true, :ack => "auto", :expires => SMQueue.calc_expiry_time(configuration.expires) }.merge(headers)
279
+ destination = configuration.name
280
+ SMQueue.dbg { [:smqueue, :send, body, headers].inspect }
281
+ connection.send destination, body, headers
282
+ rescue Exception => e
283
+ SMQueue.dbg { [:smqueue, :exception, e].inspect }
284
+ handle_error e, "Exception in SMQueue#put - #{e.message}", caller
285
+ #connection.disconnect
286
+ ensure
287
+ connection.disconnect
288
+ end
289
+ end
290
+ end
291
+ end