gz_activemessaging 0.13.1
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.
- checksums.yaml +15 -0
- data/.travis.yml +40 -0
- data/Appraisals +19 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +87 -0
- data/README.md +27 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/activemessaging.gemspec +137 -0
- data/gemfiles/activesupport23.gemfile +10 -0
- data/gemfiles/activesupport23.gemfile.lock +51 -0
- data/gemfiles/activesupport30.gemfile +11 -0
- data/gemfiles/activesupport30.gemfile.lock +53 -0
- data/gemfiles/activesupport31.gemfile +11 -0
- data/gemfiles/activesupport31.gemfile.lock +55 -0
- data/gemfiles/activesupport32.gemfile +10 -0
- data/gemfiles/activesupport32.gemfile.lock +55 -0
- data/generators/a13g_test_harness/a13g_test_harness_generator.rb +19 -0
- data/generators/a13g_test_harness/templates/active_messaging_test.rhtml +13 -0
- data/generators/a13g_test_harness/templates/active_messaging_test_controller.rb +29 -0
- data/generators/a13g_test_harness/templates/index.rhtml +17 -0
- data/generators/filter/USAGE +0 -0
- data/generators/filter/filter_generator.rb +19 -0
- data/generators/filter/templates/filter.rb +12 -0
- data/generators/filter/templates/filter_test.rb +28 -0
- data/generators/processor/USAGE +8 -0
- data/generators/processor/processor_generator.rb +31 -0
- data/generators/processor/templates/application_processor.rb +18 -0
- data/generators/processor/templates/broker.yml +140 -0
- data/generators/processor/templates/jruby_poller +117 -0
- data/generators/processor/templates/messaging.rb +12 -0
- data/generators/processor/templates/poller +25 -0
- data/generators/processor/templates/poller.rb +26 -0
- data/generators/processor/templates/processor.rb +8 -0
- data/generators/processor/templates/processor_test.rb +20 -0
- data/generators/tracer/USAGE +8 -0
- data/generators/tracer/templates/controller.rb +14 -0
- data/generators/tracer/templates/helper.rb +2 -0
- data/generators/tracer/templates/index.rhtml +4 -0
- data/generators/tracer/templates/layout.rhtml +16 -0
- data/generators/tracer/templates/trace_processor.rb +100 -0
- data/generators/tracer/tracer_generator.rb +25 -0
- data/init.rb +1 -0
- data/lib/activemessaging.rb +133 -0
- data/lib/activemessaging/adapter.rb +20 -0
- data/lib/activemessaging/adapters/amqp.rb +215 -0
- data/lib/activemessaging/adapters/asqs.rb +487 -0
- data/lib/activemessaging/adapters/base.rb +71 -0
- data/lib/activemessaging/adapters/beanstalk.rb +88 -0
- data/lib/activemessaging/adapters/jms.rb +243 -0
- data/lib/activemessaging/adapters/reliable_msg.rb +186 -0
- data/lib/activemessaging/adapters/stomp.rb +212 -0
- data/lib/activemessaging/adapters/synch.rb +95 -0
- data/lib/activemessaging/adapters/test.rb +137 -0
- data/lib/activemessaging/adapters/wmq.rb +193 -0
- data/lib/activemessaging/base_message.rb +28 -0
- data/lib/activemessaging/filter.rb +29 -0
- data/lib/activemessaging/gateway.rb +429 -0
- data/lib/activemessaging/message_sender.rb +30 -0
- data/lib/activemessaging/named_base.rb +54 -0
- data/lib/activemessaging/processor.rb +44 -0
- data/lib/activemessaging/railtie.rb +26 -0
- data/lib/activemessaging/test_helper.rb +189 -0
- data/lib/activemessaging/threaded_poller.rb +234 -0
- data/lib/activemessaging/trace_filter.rb +34 -0
- data/lib/generators/active_messaging/install/USAGE +21 -0
- data/lib/generators/active_messaging/install/install_generator.rb +39 -0
- data/lib/generators/active_messaging/install/templates/application_processor.rb +18 -0
- data/lib/generators/active_messaging/install/templates/broker.yml +139 -0
- data/lib/generators/active_messaging/install/templates/poller +24 -0
- data/lib/generators/active_messaging/install/templates/poller.rb +22 -0
- data/lib/generators/active_messaging/install/templates/threaded_poller +46 -0
- data/lib/generators/active_messaging/processor/USAGE +2 -0
- data/lib/generators/active_messaging/processor/processor_generator.rb +39 -0
- data/lib/generators/active_messaging/processor/templates/messaging.rb +12 -0
- data/lib/generators/active_messaging/processor/templates/processor.rb +8 -0
- data/lib/generators/active_messaging/processor/templates/processor_spec.rb +24 -0
- data/lib/generators/active_messaging/processor/templates/processor_test.rb +20 -0
- data/lib/tasks/start_consumers.rake +8 -0
- data/poller.rb +14 -0
- data/test/all_tests.rb +10 -0
- data/test/app/config/broker.yml +4 -0
- data/test/asqs_test.rb +125 -0
- data/test/config_test.rb +42 -0
- data/test/filter_test.rb +131 -0
- data/test/gateway_test.rb +220 -0
- data/test/jms_test.rb +64 -0
- data/test/reliable_msg_test.rb +83 -0
- data/test/stomp_test.rb +168 -0
- data/test/test_helper.rb +36 -0
- data/test/tracer_test.rb +57 -0
- metadata +202 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'activemessaging/adapter'
|
2
|
+
require 'activemessaging/base_message'
|
3
|
+
|
4
|
+
module ActiveMessaging
|
5
|
+
module Adapters
|
6
|
+
|
7
|
+
# use this as a base for implementing new connections
|
8
|
+
class BaseConnection
|
9
|
+
include ActiveMessaging::Adapter
|
10
|
+
|
11
|
+
#use the register method to add the adapter to the configurable list of supported adapters
|
12
|
+
# register :base
|
13
|
+
|
14
|
+
#configurable params
|
15
|
+
attr_accessor :reliable
|
16
|
+
|
17
|
+
#generic init method needed by a13g
|
18
|
+
def initialize cfg
|
19
|
+
end
|
20
|
+
|
21
|
+
# called to cleanly get rid of connection
|
22
|
+
def disconnect
|
23
|
+
end
|
24
|
+
|
25
|
+
# destination_name string, headers hash
|
26
|
+
# subscribe to listen on a destination
|
27
|
+
def subscribe destination_name, message_headers={}
|
28
|
+
end
|
29
|
+
|
30
|
+
# destination_name string, headers hash
|
31
|
+
# unsubscribe to listen on a destination
|
32
|
+
def unsubscribe destination_name, message_headers={}
|
33
|
+
end
|
34
|
+
|
35
|
+
# destination_name string, body string, headers hash
|
36
|
+
# send a single message to a destination
|
37
|
+
def send destination_name, message_body, message_headers={}
|
38
|
+
end
|
39
|
+
|
40
|
+
# receive a single message from any of the subscribed destinations
|
41
|
+
# check each destination once, then sleep for poll_interval
|
42
|
+
# adding options,optionally, so a poller can get certain messages (e.g. by priority)
|
43
|
+
def receive(options={})
|
44
|
+
end
|
45
|
+
|
46
|
+
# called after a message is successfully received and processed
|
47
|
+
def received message, headers={}
|
48
|
+
end
|
49
|
+
|
50
|
+
# called after a message is successfully received but unsuccessfully processed
|
51
|
+
# purpose is to return the message to the destination so receiving and processing and be attempted again
|
52
|
+
def unreceive message, headers={}
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
## I recommend having a destination object to represent each subscribed destination
|
58
|
+
# class Destination
|
59
|
+
# attr_accessor :name
|
60
|
+
#
|
61
|
+
# def to_s
|
62
|
+
# "<Base::Destination name='#{name}'>"
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
|
66
|
+
## You should also define your own message based on the BaseMessage class
|
67
|
+
# class Message < ActiveMessaging::BaseMessage
|
68
|
+
# end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# contributed by Al-Faisal El-Dajani on 11/04/2009
|
3
|
+
#
|
4
|
+
# One caveat: beanstalk does not accept the underscore '_' as a legal character in queue names.
|
5
|
+
# So in messaging.rb you'll need to modify your queue names to use something other than the underscore, a dash perhaps.
|
6
|
+
# Accepting the underscore as a valid char in queue names is an open issue in beanstalk, and should be fixed in a future version.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'beanstalk-client'
|
10
|
+
require 'activemessaging/adapters/base'
|
11
|
+
|
12
|
+
module ActiveMessaging
|
13
|
+
module Adapters
|
14
|
+
module Beanstalk
|
15
|
+
|
16
|
+
class Connection < ActiveMessaging::Adapters::BaseConnection
|
17
|
+
register :beanstalk
|
18
|
+
|
19
|
+
attr_accessor :connection, :host, :port
|
20
|
+
|
21
|
+
def initialize cfg
|
22
|
+
@host = cfg[:host] || 'localhost'
|
23
|
+
@port = cfg[:port] || 11300
|
24
|
+
|
25
|
+
@connection = ::Beanstalk::Pool.new(["#{@host}:#{@port}"])
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect
|
29
|
+
@connection.close
|
30
|
+
end
|
31
|
+
|
32
|
+
def subscribe tube, message_headers={}
|
33
|
+
@connection.watch(tube)
|
34
|
+
end
|
35
|
+
|
36
|
+
def unsubscribe tube, message_headers={}
|
37
|
+
@connection.ignore(tube)
|
38
|
+
end
|
39
|
+
|
40
|
+
def send tube, message, message_headers={}
|
41
|
+
priority = message_headers[:priority] || 65536
|
42
|
+
delay = message_headers[:delay] || 0
|
43
|
+
ttr = message_headers[:ttr] || 120
|
44
|
+
|
45
|
+
@connection.use(tube)
|
46
|
+
@connection.put(message, priority, delay, ttr)
|
47
|
+
end
|
48
|
+
|
49
|
+
def receive(options={})
|
50
|
+
message = @connection.reserve
|
51
|
+
Beanstalk::Message.new message
|
52
|
+
end
|
53
|
+
|
54
|
+
def received message, message_headers={}
|
55
|
+
message.delete
|
56
|
+
end
|
57
|
+
|
58
|
+
def unreceive message, message_headers={}
|
59
|
+
message.release
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
class Message < ActiveMessaging::BaseMessage
|
65
|
+
attr_accessor :beanstalk_job
|
66
|
+
|
67
|
+
def initialize beanstalk_job
|
68
|
+
bsh = {
|
69
|
+
'destination' => beanstalk_job.stats['tube'],
|
70
|
+
'priority' => beanstalk_job.pri,
|
71
|
+
'delay' => beanstalk_job.delay,
|
72
|
+
'ttr' => beanstalk_job.ttr
|
73
|
+
}
|
74
|
+
super(beanstalk_job.body, beanstalk_job.id, bsh, beanstalk_job.stats['tube'])
|
75
|
+
@beanstalk_job = beanstalk_job
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete
|
79
|
+
@beanstalk_job.delete
|
80
|
+
end
|
81
|
+
|
82
|
+
def release
|
83
|
+
@beanstalk_job.release
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
if defined?(JRUBY_VERSION)
|
2
|
+
#require 'java'
|
3
|
+
include Java
|
4
|
+
require 'activemq'
|
5
|
+
|
6
|
+
begin
|
7
|
+
java_import javax.naming.InitialContext
|
8
|
+
java_import javax.jms.MessageListener
|
9
|
+
rescue NameError
|
10
|
+
raise LoadError, "ActiveMessaging::Adapter::Jms needs Jave EE classes on the CLASSPATH"
|
11
|
+
end
|
12
|
+
|
13
|
+
module ActiveMessaging
|
14
|
+
module Adapters
|
15
|
+
module Jms
|
16
|
+
|
17
|
+
class Connection
|
18
|
+
include ActiveMessaging::Adapter
|
19
|
+
register :jms
|
20
|
+
|
21
|
+
attr_accessor :reliable, :connection, :session, :producers, :consumers
|
22
|
+
|
23
|
+
def initialize cfg={}
|
24
|
+
@url = cfg[:url]
|
25
|
+
@login = cfg[:login]
|
26
|
+
@passcode = cfg[:passcode]
|
27
|
+
#initialize our connection factory
|
28
|
+
if cfg.has_key? :connection_factory
|
29
|
+
#this initialize is probably activemq specific. There might be a more generic
|
30
|
+
#way of getting this without resorting to jndi lookup.
|
31
|
+
eval <<-end_eval
|
32
|
+
@connection_factory = Java::#{cfg[:connection_factory]}.new(@login, @passcode, @url)
|
33
|
+
end_eval
|
34
|
+
elsif cfg.has_key? :jndi
|
35
|
+
@connection_factory = javax.naming.InitialContext.new().lookup(cfg[:jndi])
|
36
|
+
else
|
37
|
+
raise "Either jndi or connection_factory has to be set in the config."
|
38
|
+
end
|
39
|
+
raise "Connection factory could not be initialized." if @connection_factory.nil?
|
40
|
+
|
41
|
+
@connection = @connection_factory.create_connection()
|
42
|
+
@session = @connection.createSession(false, 1)
|
43
|
+
@destinations = []
|
44
|
+
@producers = {}
|
45
|
+
@consumers = {}
|
46
|
+
@connection.start
|
47
|
+
end
|
48
|
+
|
49
|
+
def subscribe queue_name, headers={}
|
50
|
+
queue_name = check_destination_type queue_name, headers
|
51
|
+
find_or_create_consumer queue_name, headers
|
52
|
+
end
|
53
|
+
|
54
|
+
def unsubscribe queue_name, headers={}
|
55
|
+
queue_name = check_destination_type queue_name, headers
|
56
|
+
consumer = @consumers[queue_name]
|
57
|
+
unless consumer.nil?
|
58
|
+
consumer.close
|
59
|
+
@consumers.delete queue_name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def send queue_name, body, headers={}
|
64
|
+
queue_name = check_destination_type queue_name, headers
|
65
|
+
producer = find_or_create_producer queue_name, headers.symbolize_keys
|
66
|
+
message = @session.create_text_message body
|
67
|
+
headers.stringify_keys.each do |key, value|
|
68
|
+
if ['id', 'message-id', 'JMSMessageID'].include? key
|
69
|
+
message.setJMSMessageID value.to_s
|
70
|
+
elsif ['correlation-id', 'JMSCorrelationID'].include? key
|
71
|
+
message.setJMSCorrelationID value.to_s
|
72
|
+
elsif ['expires', 'JMSExpiration'].include? key
|
73
|
+
message.setJMSExpiration value.to_i
|
74
|
+
elsif ['persistent', 'JMSDeliveryMode'].include? key
|
75
|
+
message.setJMSDeliveryMode(value ? 2 : 1)
|
76
|
+
elsif ['priority', 'JMSPriority'].include? key
|
77
|
+
message.setJMSPriority value.to_i
|
78
|
+
elsif ['reply-to', 'JMSReplyTo'].include? key
|
79
|
+
message.setJMSReplyTo value.to_s
|
80
|
+
elsif ['type', 'JMSType'].include? key
|
81
|
+
message.setJMSType value.to_s
|
82
|
+
else #is this the most appropriate thing to do here?
|
83
|
+
message.set_string_property key, value.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
producer.send message
|
87
|
+
end
|
88
|
+
|
89
|
+
def receive(options={})
|
90
|
+
queue_name = options[:queue_name]
|
91
|
+
headers = options[:headers] || {}
|
92
|
+
receive_message(queue_name, headers)
|
93
|
+
end
|
94
|
+
|
95
|
+
def receive_message(queue_name=nil, headers={})
|
96
|
+
if queue_name.nil?
|
97
|
+
@consumers.find do |k, c|
|
98
|
+
message = c.receive(1)
|
99
|
+
return condition_message(message) unless message.nil?
|
100
|
+
end
|
101
|
+
else
|
102
|
+
consumer = subscribe(queue_name, headers)
|
103
|
+
message = consumer.receive(1)
|
104
|
+
unsubscribe(queue_name, headers)
|
105
|
+
condition_message(message)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def received message, headers={}
|
110
|
+
#do nothing
|
111
|
+
end
|
112
|
+
|
113
|
+
def unreceive message, headers={}
|
114
|
+
# do nothing
|
115
|
+
end
|
116
|
+
|
117
|
+
def close
|
118
|
+
@consumers.each {|k, c| c.stop }
|
119
|
+
@connection.stop
|
120
|
+
@session.close
|
121
|
+
@connection.close
|
122
|
+
@connection = nil
|
123
|
+
@session = nil
|
124
|
+
@consumers = {}
|
125
|
+
@producers = {}
|
126
|
+
end
|
127
|
+
|
128
|
+
def find_or_create_producer queue_name, headers={}
|
129
|
+
producer = @producers[queue_name]
|
130
|
+
if producer.nil?
|
131
|
+
destination = find_or_create_destination queue_name, headers
|
132
|
+
producer = @session.create_producer destination
|
133
|
+
end
|
134
|
+
producer
|
135
|
+
end
|
136
|
+
|
137
|
+
def find_or_create_consumer queue_name, headers={}
|
138
|
+
consumer = @consumers[queue_name]
|
139
|
+
if consumer.nil?
|
140
|
+
destination = find_or_create_destination queue_name, headers
|
141
|
+
if headers.symbolize_keys.has_key? :selector
|
142
|
+
consumer = @session.create_consumer destination, headers.symbolize_keys[:selector]
|
143
|
+
else
|
144
|
+
consumer = @session.create_consumer destination
|
145
|
+
end
|
146
|
+
|
147
|
+
@consumers[queue_name] = consumer
|
148
|
+
end
|
149
|
+
consumer
|
150
|
+
end
|
151
|
+
|
152
|
+
def find_or_create_destination queue_name, headers={}
|
153
|
+
destination = find_destination queue_name, headers[:destination_type]
|
154
|
+
if destination.nil?
|
155
|
+
if headers.symbolize_keys[:destination_type] == :topic
|
156
|
+
destination = @session.create_topic(queue_name.to_s)
|
157
|
+
@destinations << destination
|
158
|
+
elsif headers.symbolize_keys[:destination_type] == :queue
|
159
|
+
destination = @session.create_queue(queue_name.to_s)
|
160
|
+
@destinations << destination
|
161
|
+
else
|
162
|
+
raise "headers[:destination_type] must be either :queue or :topic. was #{headers[:destination_type]}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
destination
|
166
|
+
end
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
def condition_message message
|
171
|
+
message.class.class_eval {
|
172
|
+
alias_method :body, :text unless method_defined? :body
|
173
|
+
|
174
|
+
def headers
|
175
|
+
destination.to_s =~ %r{(queue|topic)://(.*)}
|
176
|
+
puts "/#{$1}/#{$2}"
|
177
|
+
{'destination' => "/#{$1}/#{$2}"}
|
178
|
+
end
|
179
|
+
|
180
|
+
def matches_subscription?(subscription)
|
181
|
+
self.headers['destination'].to_s == subscription.value.to_s
|
182
|
+
end
|
183
|
+
|
184
|
+
} unless message.nil? || message.respond_to?(:headers)
|
185
|
+
message
|
186
|
+
end
|
187
|
+
|
188
|
+
def check_destination_type queue_name, headers
|
189
|
+
stringy_h = headers.stringify_keys
|
190
|
+
if queue_name =~ %r{^/(topic|queue)/(.*)$} && !stringy_h.has_key?('destination_type')
|
191
|
+
headers['destination_type'] = $1.to_sym
|
192
|
+
return $2
|
193
|
+
else
|
194
|
+
raise "Must specify destination type either with either 'headers[\'destination_type\']=[:queue|:topic]' or /[topic|queue]/destination_name for queue name '#{queue_name}'" unless [:topic, :queue].include? stringy_h['destination_type']
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def find_destination queue_name, type
|
199
|
+
@destinations.find do |d|
|
200
|
+
if d.is_a?(javax.jms.Topic) && type == :topic
|
201
|
+
d.topic_name == queue_name
|
202
|
+
elsif d.is_a?(javax.jms.Queue) && type == :queue
|
203
|
+
d.queue_name == queue_name
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
#
|
209
|
+
# class RubyMessageListener
|
210
|
+
# include javax.jms.MessageListener
|
211
|
+
#
|
212
|
+
# def initialize(connection, destination, name)
|
213
|
+
# @connection = connection
|
214
|
+
# @destination = destination
|
215
|
+
# @name = name
|
216
|
+
# end
|
217
|
+
#
|
218
|
+
# def onMessage(msg)
|
219
|
+
# headers = {}
|
220
|
+
# enm = msg.getPropertyNames
|
221
|
+
# while enm.hasMoreElements
|
222
|
+
# key = enm.nextElement
|
223
|
+
# headers[key.to_s] = msg.getStringProperty(key)
|
224
|
+
# end
|
225
|
+
# Gateway.dispatch(JMSRecvMessage.new(headers,msg.text,@name))
|
226
|
+
# rescue => e
|
227
|
+
# STDERR.puts "something went really wrong with a message: #{e.inspect}"
|
228
|
+
# end
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# class JMSRecvMessage < ActiveMessaging::Adapters::Base::Message
|
232
|
+
# def initialize(headers, body, name, command='MESSAGE')
|
233
|
+
# @headers = headers
|
234
|
+
# @body = body
|
235
|
+
# @command = command
|
236
|
+
# @headers['destination'] = name
|
237
|
+
# end
|
238
|
+
# end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'reliable-msg'
|
2
|
+
|
3
|
+
require 'activemessaging/adapters/base'
|
4
|
+
|
5
|
+
# add access to the queue_manager to use for adding transactions
|
6
|
+
module ReliableMsg
|
7
|
+
|
8
|
+
class Client
|
9
|
+
|
10
|
+
def queue_manager
|
11
|
+
qm
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
module ActiveMessaging
|
19
|
+
module Adapters
|
20
|
+
class ReliableMsgConnection < ActiveMessaging::Adapters::BaseConnection
|
21
|
+
|
22
|
+
THREAD_OLD_TXS = :a13g_reliable_msg_old_txs
|
23
|
+
|
24
|
+
QUEUE_PARAMS = [:expires,:delivery,:priority,:max_deliveries,:drb_uri,:tx_timeout,:connect_count]
|
25
|
+
TOPIC_PARAMS = [:expires,:drb_uri,:tx_timeout,:connect_count]
|
26
|
+
|
27
|
+
register :reliable_msg
|
28
|
+
|
29
|
+
#configurable params
|
30
|
+
attr_accessor :subscriptions, :destinations, :poll_interval, :current_subscription, :tx_timeout
|
31
|
+
|
32
|
+
#generic init method needed by a13g
|
33
|
+
def initialize cfg
|
34
|
+
@poll_interval = cfg[:poll_interval] || 1
|
35
|
+
@reliable = cfg[:reliable] || true
|
36
|
+
@tx_timeout = cfg[:tx_timeout] || ::ReliableMsg::Client::DEFAULT_TX_TIMEOUT
|
37
|
+
|
38
|
+
@subscriptions = {}
|
39
|
+
@destinations = {}
|
40
|
+
@current_subscription = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
# called to cleanly get rid of connection
|
44
|
+
def disconnect
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# destination_name string, headers hash
|
49
|
+
# subscribe to listen on a destination
|
50
|
+
# use '/destination-type/name' convetion, like stomp
|
51
|
+
def subscribe destination_name, message_headers={}
|
52
|
+
get_or_create_destination(destination_name, message_headers)
|
53
|
+
if subscriptions.has_key? destination_name
|
54
|
+
subscriptions[destination_name].add
|
55
|
+
else
|
56
|
+
subscriptions[destination_name] = Subscription.new(destination_name, message_headers)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# destination_name string, headers hash
|
61
|
+
# unsubscribe to listen on a destination
|
62
|
+
def unsubscribe destination_name, message_headers={}
|
63
|
+
subscriptions[destination_name].remove
|
64
|
+
subscriptions.delete(destination_name) if subscriptions[destination_name].count <= 0
|
65
|
+
end
|
66
|
+
|
67
|
+
# destination_name string, body string, headers hash
|
68
|
+
# send a single message to a destination
|
69
|
+
def send destination_name, message_body, message_headers={}
|
70
|
+
dest = get_or_create_destination(destination_name)
|
71
|
+
begin
|
72
|
+
dest.put message_body, message_headers
|
73
|
+
rescue Object=>err
|
74
|
+
raise err unless reliable
|
75
|
+
puts "send failed, will retry in #{@poll_interval} seconds"
|
76
|
+
sleep @poll_interval
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_or_create_destination destination_name, message_headers={}
|
81
|
+
return destinations[destination_name] if destinations.has_key? destination_name
|
82
|
+
dd = /^\/(queue|topic)\/(.*)$/.match(destination_name)
|
83
|
+
rm_class = dd[1].titleize
|
84
|
+
message_headers.delete("id")
|
85
|
+
dest_headers = message_headers.reject {|k,v| rm_class == 'Queue' ? !QUEUE_PARAMS.include?(k) : !TOPIC_PARAMS.include?(k)}
|
86
|
+
rm_dest = "ReliableMsg::#{rm_class}".constantize.new(dd[2], dest_headers)
|
87
|
+
destinations[destination_name] = rm_dest
|
88
|
+
end
|
89
|
+
|
90
|
+
# receive a single message from any of the subscribed destinations
|
91
|
+
# check each destination once, then sleep for poll_interval
|
92
|
+
def receive(options={})
|
93
|
+
|
94
|
+
raise "No subscriptions to receive messages from." if (subscriptions.nil? || subscriptions.empty?)
|
95
|
+
start = current_subscription
|
96
|
+
while true
|
97
|
+
self.current_subscription = ((current_subscription < subscriptions.length-1) ? current_subscription + 1 : 0)
|
98
|
+
sleep poll_interval if (current_subscription == start)
|
99
|
+
destination_name = subscriptions.keys.sort[current_subscription]
|
100
|
+
destination = destinations[destination_name]
|
101
|
+
unless destination.nil?
|
102
|
+
# from the way we use this, assume this is the start of a transaction,
|
103
|
+
# there should be no current transaction
|
104
|
+
ctx = Thread.current[::ReliableMsg::Client::THREAD_CURRENT_TX]
|
105
|
+
raise "There should not be an existing reliable-msg transaction. #{ctx.inspect}" if ctx
|
106
|
+
|
107
|
+
# start a new transaction
|
108
|
+
@tx = {:qm=>destination.queue_manager}
|
109
|
+
@tx[:tid] = @tx[:qm].begin @tx_timeout
|
110
|
+
Thread.current[::ReliableMsg::Client::THREAD_CURRENT_TX] = @tx
|
111
|
+
begin
|
112
|
+
|
113
|
+
# now call a get on the destination - it will use the transaction
|
114
|
+
#the commit or the abort will occur in the received or unreceive methods
|
115
|
+
reliable_msg = destination.get subscriptions[destination_name].headers[:selector]
|
116
|
+
@tx[:qm].commit(@tx[:tid]) if reliable_msg.nil?
|
117
|
+
|
118
|
+
rescue Object=>err
|
119
|
+
#abort the transaction on error
|
120
|
+
@tx[:qm].abort(@tx[:tid])
|
121
|
+
|
122
|
+
raise err unless reliable
|
123
|
+
puts "receive failed, will retry in #{@poll_interval} seconds"
|
124
|
+
sleep poll_interval
|
125
|
+
end
|
126
|
+
return Message.new(reliable_msg.object, reliable_msg.id, reliable_msg.headers, destination_name, @tx) if reliable_msg
|
127
|
+
|
128
|
+
Thread.current[::ReliableMsg::Client::THREAD_CURRENT_TX] = nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# called after a message is successfully received and processed
|
134
|
+
def received message, headers={}
|
135
|
+
begin
|
136
|
+
message.transaction[:qm].commit(message.transaction[:tid])
|
137
|
+
rescue Object=>ex
|
138
|
+
puts "received failed: #{ex.message}"
|
139
|
+
ensure
|
140
|
+
Thread.current[::ReliableMsg::Client::THREAD_CURRENT_TX] = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
# called after a message is successfully received and processed
|
146
|
+
def unreceive message, headers={}
|
147
|
+
begin
|
148
|
+
message.transaction[:qm].abort(message.transaction[:tid])
|
149
|
+
rescue Object=>ex
|
150
|
+
puts "unreceive failed: #{ex.message}"
|
151
|
+
ensure
|
152
|
+
Thread.current[::ReliableMsg::Client::THREAD_CURRENT_TX] = nil
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
class Subscription
|
159
|
+
attr_accessor :name, :headers, :count
|
160
|
+
|
161
|
+
def initialize(destination, headers={}, count=1)
|
162
|
+
@destination, @headers, @count = destination, headers, count
|
163
|
+
end
|
164
|
+
|
165
|
+
def add
|
166
|
+
@count += 1
|
167
|
+
end
|
168
|
+
|
169
|
+
def remove
|
170
|
+
@count -= 1
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
class Message < ActiveMessaging::BaseMessage
|
176
|
+
attr_accessor :transaction
|
177
|
+
|
178
|
+
def initialize body, id, headers, destination, transaction=nil
|
179
|
+
super(body, id, headers, destination)
|
180
|
+
@transaction = transaction
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|