gz_activemessaging 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|