activemessaging 0.6.1 → 0.7.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.
- data/README +22 -0
- data/Rakefile +48 -26
- data/VERSION +1 -0
- data/activemessaging.gemspec +113 -0
- data/generators/filter/templates/filter_test.rb +1 -1
- data/generators/processor/templates/broker.yml +18 -7
- data/init.rb +3 -0
- data/lib/activemessaging.rb +0 -3
- data/lib/activemessaging/adapters/asqs.rb +69 -61
- data/lib/activemessaging/adapters/base.rb +51 -63
- data/lib/activemessaging/adapters/beanstalk.rb +88 -0
- data/lib/activemessaging/adapters/jms.rb +7 -7
- data/lib/activemessaging/adapters/reliable_msg.rb +136 -140
- data/lib/activemessaging/adapters/stomp.rb +90 -22
- data/lib/activemessaging/adapters/test.rb +7 -25
- data/lib/activemessaging/adapters/wmq.rb +7 -16
- data/lib/activemessaging/base_message.rb +19 -0
- data/lib/activemessaging/gateway.rb +38 -41
- data/lib/activemessaging/test_helper.rb +4 -9
- 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 +102 -0
- data/test/config_test.rb +42 -0
- data/test/filter_test.rb +131 -0
- data/test/gateway_test.rb +195 -0
- data/test/jms_test.rb +61 -0
- data/test/reliable_msg_test.rb +83 -0
- data/test/stomp_test.rb +131 -0
- data/test/test_helper.rb +24 -0
- data/test/tracer_test.rb +57 -0
- metadata +69 -53
- data/messaging.rb.example +0 -5
@@ -1,41 +1,82 @@
|
|
1
1
|
require 'stomp'
|
2
2
|
|
3
|
+
require 'activemessaging/adapters/base'
|
4
|
+
|
3
5
|
module ActiveMessaging
|
4
6
|
module Adapters
|
5
7
|
module Stomp
|
6
|
-
|
7
|
-
class Connection < ::
|
8
|
-
include ActiveMessaging::Adapter
|
8
|
+
|
9
|
+
class Connection < ActiveMessaging::Adapters::BaseConnection
|
9
10
|
register :stomp
|
10
11
|
|
11
|
-
attr_accessor :
|
12
|
+
attr_accessor :stomp_connection, :retryMax, :deadLetterQueue, :configuration
|
12
13
|
|
13
14
|
def initialize(cfg)
|
14
15
|
@retryMax = cfg[:retryMax] || 0
|
15
16
|
@deadLetterQueue = cfg[:deadLetterQueue] || nil
|
16
|
-
|
17
|
+
|
17
18
|
cfg[:login] ||= ""
|
18
19
|
cfg[:passcode] ||= ""
|
19
20
|
cfg[:host] ||= "localhost"
|
20
21
|
cfg[:port] ||= "61613"
|
21
|
-
cfg[:reliable]
|
22
|
+
cfg[:reliable] = cfg[:reliable].nil? ? TRUE : cfg[:reliable].nil?
|
22
23
|
cfg[:reconnectDelay] ||= 5
|
23
24
|
cfg[:clientId] ||= nil
|
24
|
-
|
25
|
-
if cfg[:clientId]
|
26
|
-
super(cfg[:login],cfg[:passcode],cfg[:host],cfg[:port].to_i,cfg[:reliable],cfg[:reconnectDelay],cfg[:clientId])
|
27
|
-
else
|
28
|
-
super(cfg[:login],cfg[:passcode],cfg[:host],cfg[:port].to_i,cfg[:reliable],cfg[:reconnectDelay])
|
29
|
-
end
|
30
25
|
|
26
|
+
# hold on to the config
|
27
|
+
@configuration = cfg
|
28
|
+
|
29
|
+
# create a new stomp connection
|
30
|
+
connect_headers = {}
|
31
|
+
connect_headers['client-id'] = cfg[:clientId] if cfg[:clientId]
|
32
|
+
@stomp_connection = ::Stomp::Connection.new(cfg[:login],cfg[:passcode],cfg[:host],cfg[:port].to_i,cfg[:reliable],cfg[:reconnectDelay], connect_headers)
|
31
33
|
end
|
32
|
-
|
34
|
+
|
35
|
+
# called to cleanly get rid of connection
|
36
|
+
def disconnect
|
37
|
+
@stomp_connection.disconnect
|
38
|
+
end
|
39
|
+
|
40
|
+
# destination_name string, headers hash
|
41
|
+
# subscribe to listen on a destination
|
42
|
+
def subscribe destination_name, message_headers={}
|
43
|
+
@stomp_connection.subscribe(destination_name, message_headers)
|
44
|
+
end
|
45
|
+
|
46
|
+
# destination_name string, headers hash
|
47
|
+
# unsubscribe to listen on a destination
|
48
|
+
def unsubscribe destination_name, message_headers={}
|
49
|
+
@stomp_connection.unsubscribe(destination_name, message_headers)
|
50
|
+
end
|
51
|
+
|
52
|
+
# destination_name string, body string, headers hash
|
53
|
+
# send a single message to a destination
|
54
|
+
def send destination_name, message_body, message_headers={}
|
55
|
+
stomp_publish(destination_name, message_body, message_headers)
|
56
|
+
end
|
57
|
+
|
58
|
+
# receive a single message from any of the subscribed destinations
|
59
|
+
# check each destination once, then sleep for poll_interval
|
60
|
+
def receive
|
61
|
+
m = @stomp_connection.receive
|
62
|
+
Message.new(m) if m
|
63
|
+
end
|
64
|
+
|
33
65
|
def received message, headers={}
|
34
66
|
#check to see if the ack mode for this subscription is auto or client
|
35
67
|
# if the ack mode is client, send an ack
|
36
68
|
if (headers[:ack] === 'client')
|
37
|
-
ack_headers = message.headers.has_key?(:transaction) ? message.headers[:transaction] : {}
|
38
|
-
ack
|
69
|
+
ack_headers = message.headers.has_key?(:transaction) ? { :transaction=>message.headers[:transaction]} : {}
|
70
|
+
@stomp_connection.ack(message.headers['message-id'], ack_headers)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# send has been deprecated in latest stomp gem (as it should be)
|
75
|
+
def stomp_publish(destination_name="", message_body="", message_headers={})
|
76
|
+
if @stomp_connection.respond_to?(:publish)
|
77
|
+
@stomp_connection.publish(destination_name, message_body, message_headers)
|
78
|
+
else
|
79
|
+
@stomp_connection.send(destination_name, message_body, message_headers)
|
39
80
|
end
|
40
81
|
end
|
41
82
|
|
@@ -44,7 +85,7 @@ module ActiveMessaging
|
|
44
85
|
transaction_id = "transaction-#{message.headers['message-id']}-#{retry_count}"
|
45
86
|
|
46
87
|
# start a transaction, send the message back to the original destination
|
47
|
-
|
88
|
+
@stomp_connection.begin(transaction_id)
|
48
89
|
begin
|
49
90
|
|
50
91
|
if @retryMax > 0
|
@@ -53,6 +94,8 @@ module ActiveMessaging
|
|
53
94
|
retry_headers.delete('content-length')
|
54
95
|
retry_headers.delete('content-type')
|
55
96
|
|
97
|
+
retry_destination = retry_headers.delete('destination')
|
98
|
+
|
56
99
|
if retry_count < @retryMax
|
57
100
|
# now send the message back to the destination
|
58
101
|
# set the headers for message id, priginal message id, and retry count
|
@@ -65,13 +108,15 @@ module ActiveMessaging
|
|
65
108
|
retry_headers['a13g-retry-count'] = retry_count + 1
|
66
109
|
|
67
110
|
# send the updated message to retry in the same transaction
|
68
|
-
self.
|
111
|
+
self.stomp_publish(retry_destination, message.body, retry_headers)
|
69
112
|
|
70
113
|
elsif retry_count >= @retryMax && @deadLetterQueue
|
71
|
-
# send the 'poison pill' message to the dead letter queue
|
114
|
+
# send the 'poison pill' message to the dead letter queue - make it persistent by default
|
72
115
|
retry_headers['a13g-original-destination'] = retry_headers.delete('destination')
|
116
|
+
retry_headers['persistent'] = true
|
73
117
|
retry_headers.delete('message-id')
|
74
|
-
|
118
|
+
|
119
|
+
self.stomp_publish(@deadLetterQueue, message.body, retry_headers)
|
75
120
|
end
|
76
121
|
|
77
122
|
end
|
@@ -79,14 +124,14 @@ module ActiveMessaging
|
|
79
124
|
#check to see if the ack mode is client, and if it is, ack it in this transaction
|
80
125
|
if (headers[:ack] === 'client')
|
81
126
|
# ack the original message
|
82
|
-
|
127
|
+
@stomp_connection.ack(message.headers['message-id'], message.headers.stringify_keys.merge('transaction'=>transaction_id))
|
83
128
|
end
|
84
129
|
|
85
130
|
# now commit the transaction
|
86
|
-
|
131
|
+
@stomp_connection.commit transaction_id
|
87
132
|
rescue Exception=>exc
|
88
133
|
# if there is an error, try to abort the transaction, then raise the error
|
89
|
-
|
134
|
+
@stomp_connection.abort transaction_id
|
90
135
|
raise exc
|
91
136
|
end
|
92
137
|
|
@@ -94,6 +139,29 @@ module ActiveMessaging
|
|
94
139
|
|
95
140
|
end
|
96
141
|
|
142
|
+
class Message < ActiveMessaging::BaseMessage
|
143
|
+
|
144
|
+
def initialize(msg)
|
145
|
+
super(msg.body, msg.headers['message-id'], msg.headers, msg.headers['destination'])
|
146
|
+
end
|
147
|
+
|
148
|
+
def matches_subscription?(subscription)
|
149
|
+
# if the subscription has been specified in the headers, rely on this
|
150
|
+
if self.headers['subscription'] && subscription.subscribe_headers['id']
|
151
|
+
self.headers['subscription'].to_s == subscription.subscribe_headers['id'].to_s
|
152
|
+
|
153
|
+
# see if the destination uses a wildcard representation
|
154
|
+
elsif subscription.destination.wildcard
|
155
|
+
self.destination.to_s =~ subscription.destination.wildcard
|
156
|
+
|
157
|
+
# no subscription id? no wildcard? use the name of the destination as a straight match
|
158
|
+
else
|
159
|
+
self.destination.to_s == subscription.destination.value.to_s
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
97
165
|
end
|
98
166
|
end
|
99
167
|
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
require 'activemessaging/
|
1
|
+
require 'activemessaging/adapters/base'
|
2
2
|
|
3
3
|
module ActiveMessaging
|
4
4
|
module Adapters
|
5
5
|
module Test
|
6
6
|
|
7
|
-
class Connection
|
8
|
-
include ActiveMessaging::Adapter
|
7
|
+
class Connection < ActiveMessaging::Adapters::BaseConnection
|
9
8
|
register :test
|
10
9
|
|
11
10
|
attr_accessor :config, :subscriptions, :destinations, :connected, :received_messages, :unreceived_messages
|
@@ -42,7 +41,7 @@ module ActiveMessaging
|
|
42
41
|
def send destination_name, message_body, message_headers={}
|
43
42
|
open_destination destination_name
|
44
43
|
destination = find_destination destination_name
|
45
|
-
destination.send Message.new(
|
44
|
+
destination.send Message.new(message_body, nil, message_headers, destination_name)
|
46
45
|
end
|
47
46
|
|
48
47
|
def receive
|
@@ -52,15 +51,6 @@ module ActiveMessaging
|
|
52
51
|
destination.receive unless destination.nil?
|
53
52
|
end
|
54
53
|
|
55
|
-
# should not be 2 defs for receive, this isn't java, ya know? -Andrew
|
56
|
-
# def receive destination_name, headers={}
|
57
|
-
# subscribe destination_name, headers
|
58
|
-
# destination = find_destination destination_name
|
59
|
-
# message = destination.receive
|
60
|
-
# unsubscribe destination_name, headers
|
61
|
-
# message
|
62
|
-
# end
|
63
|
-
|
64
54
|
def received message, headers={}
|
65
55
|
@received_messages << message
|
66
56
|
end
|
@@ -71,8 +61,9 @@ module ActiveMessaging
|
|
71
61
|
|
72
62
|
#test helper methods
|
73
63
|
def find_message destination_name, body
|
64
|
+
|
74
65
|
all_messages.find do |m|
|
75
|
-
m.
|
66
|
+
m.destination == destination_name && if body.is_a?(Regexp)
|
76
67
|
m.body =~ body
|
77
68
|
else
|
78
69
|
m.body == body.to_s
|
@@ -138,18 +129,9 @@ module ActiveMessaging
|
|
138
129
|
end
|
139
130
|
end
|
140
131
|
|
141
|
-
class Message
|
142
|
-
attr_accessor :headers, :body, :command
|
143
|
-
|
144
|
-
def initialize headers, id, body, response, destination, command='MESSAGE'
|
145
|
-
@headers, @body, @command = headers, body, command
|
146
|
-
headers['destination'] = destination.name
|
147
|
-
end
|
148
|
-
|
149
|
-
def to_s
|
150
|
-
"<Test::Message body='#{body}' headers='#{headers.inspect}' command='#{command}' >"
|
151
|
-
end
|
132
|
+
class Message < ActiveMessaging::BaseMessage
|
152
133
|
end
|
134
|
+
|
153
135
|
end
|
154
136
|
end
|
155
137
|
end
|
@@ -19,18 +19,16 @@
|
|
19
19
|
#
|
20
20
|
require 'wmq/wmq'
|
21
21
|
|
22
|
+
require 'activemessaging/adapters/base'
|
23
|
+
|
22
24
|
module ActiveMessaging
|
23
25
|
module Adapters
|
24
26
|
module Adapter
|
25
27
|
|
26
28
|
# Connection class needed by a13g
|
27
|
-
class Connection
|
28
|
-
include ActiveMessaging::Adapter
|
29
|
+
class Connection < ActiveMessaging::Adapters::BaseConnection
|
29
30
|
register :wmq
|
30
31
|
|
31
|
-
# Needed by a13g but never used within this adapter
|
32
|
-
attr_accessor :reliable
|
33
|
-
|
34
32
|
# Generic init method needed by a13g
|
35
33
|
def initialize(cfg)
|
36
34
|
# Set default values
|
@@ -147,22 +145,15 @@ module ActiveMessaging
|
|
147
145
|
|
148
146
|
# Message class needed by a13g (based on the same Message class in Stomp adapter)
|
149
147
|
# Contains a reference to the MQ message object ;-) !
|
150
|
-
class Message
|
151
|
-
|
152
|
-
attr_accessor :
|
148
|
+
class Message < ActiveMessaging::BaseMessage
|
149
|
+
|
150
|
+
attr_accessor :wmq_message
|
153
151
|
|
154
152
|
def initialize(wmq_message, q_name)
|
153
|
+
super(wmq_message.data, wmq_message.descriptor[:msg_id], {'destination' => q_name}, q_name)
|
155
154
|
@wmq_message = wmq_message
|
156
|
-
|
157
|
-
# Needed by a13g
|
158
|
-
@headers = {'destination' => q_name}
|
159
|
-
@body = wmq_message.data
|
160
|
-
@command = 'MESSAGE'
|
161
155
|
end
|
162
156
|
|
163
|
-
def to_s
|
164
|
-
"<Adapter::Message headers=#{@headers.inspect} body='#{@body}' command='#{@command}' wmq_message=#{@wmq_message}>"
|
165
|
-
end
|
166
157
|
end
|
167
158
|
|
168
159
|
private
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveMessaging
|
2
|
+
|
3
|
+
class BaseMessage
|
4
|
+
attr_accessor :body, :id, :headers, :destination
|
5
|
+
|
6
|
+
def initialize(body=nil, id=nil, headers={}, destination=nil)
|
7
|
+
@body, @id, @headers, @destination = body, id, headers, destination
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches_subscription?(subscription)
|
11
|
+
self.destination.to_s == subscription.destination.value.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"<#{self.class.name} id='#{id}' headers='#{headers.inspect}' body='#{body}' >"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -189,13 +189,14 @@ module ActiveMessaging
|
|
189
189
|
|
190
190
|
def prepare_application
|
191
191
|
if defined? Rails
|
192
|
-
Dispatcher.prepare_application_for_dispatch
|
192
|
+
# Dispatcher.prepare_application_for_dispatch
|
193
|
+
ActiveRecord::Base.verify_active_connections!
|
193
194
|
end
|
194
195
|
end
|
195
196
|
|
196
197
|
def reset_application
|
197
198
|
if defined? Rails
|
198
|
-
Dispatcher.reset_application_after_dispatch
|
199
|
+
# Dispatcher.reset_application_after_dispatch
|
199
200
|
end
|
200
201
|
end
|
201
202
|
|
@@ -215,39 +216,33 @@ module ActiveMessaging
|
|
215
216
|
end
|
216
217
|
|
217
218
|
def _dispatch(message)
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
}
|
233
|
-
begin
|
234
|
-
execute_filter_chain(:incoming, message, routing) do |m|
|
235
|
-
result = subscription.processor_class.new.process!(m)
|
236
|
-
end
|
237
|
-
rescue ActiveMessaging::AbortMessageException
|
238
|
-
abort_message subscription, message
|
239
|
-
abort = true
|
240
|
-
return
|
241
|
-
ensure
|
242
|
-
acknowledge_message subscription, message unless abort
|
219
|
+
abort = false
|
220
|
+
processed = false
|
221
|
+
|
222
|
+
subscriptions.each do |key, subscription|
|
223
|
+
if message.matches_subscription?(subscription) then
|
224
|
+
processed = true
|
225
|
+
routing = {
|
226
|
+
:receiver=>subscription.processor_class,
|
227
|
+
:destination=>subscription.destination,
|
228
|
+
:direction => :incoming
|
229
|
+
}
|
230
|
+
begin
|
231
|
+
execute_filter_chain(:incoming, message, routing) do |m|
|
232
|
+
result = subscription.processor_class.new.process!(m)
|
243
233
|
end
|
234
|
+
rescue ActiveMessaging::AbortMessageException
|
235
|
+
abort_message subscription, message
|
236
|
+
abort = true
|
237
|
+
return
|
238
|
+
ensure
|
239
|
+
acknowledge_message subscription, message unless abort
|
244
240
|
end
|
245
241
|
end
|
246
|
-
|
247
|
-
ActiveMessaging.logger.error("No-one responded to #{message}") unless processed
|
248
|
-
else
|
249
|
-
ActiveMessaging.logger.error('Unknown message command: ' + message.inspect)
|
250
242
|
end
|
243
|
+
|
244
|
+
ActiveMessaging.logger.error("No-one responded to #{message}") unless processed
|
245
|
+
|
251
246
|
end
|
252
247
|
|
253
248
|
# acknowledge_message is called when the message has been processed w/o error by at least one processor
|
@@ -268,7 +263,7 @@ module ActiveMessaging
|
|
268
263
|
|
269
264
|
def destination destination_name, destination, publish_headers={}, broker='default'
|
270
265
|
raise "You already defined #{destination_name} to #{named_destinations[destination_name].value}" if named_destinations.has_key?(destination_name)
|
271
|
-
named_destinations[destination_name] = Destination.new
|
266
|
+
named_destinations[destination_name] = Destination.new(destination_name, destination, publish_headers, broker)
|
272
267
|
end
|
273
268
|
|
274
269
|
alias queue destination
|
@@ -385,16 +380,10 @@ module ActiveMessaging
|
|
385
380
|
@destination, @processor_class, @subscribe_headers = destination, processor_class, subscribe_headers
|
386
381
|
subscribe_headers['id'] = processor_class.name.underscore unless subscribe_headers.key? 'id'
|
387
382
|
end
|
388
|
-
|
389
|
-
def matches?(message)
|
390
|
-
message.headers['destination'].to_s == @destination.value.to_s
|
391
|
-
end
|
392
|
-
|
383
|
+
|
393
384
|
def subscribe
|
394
385
|
ActiveMessaging.logger.error "=> Subscribing to #{destination.value} (processed by #{processor_class})"
|
395
386
|
Gateway.connection(@destination.broker_name).subscribe(@destination.value, subscribe_headers)
|
396
|
-
# FIXME (uwe): Not sure why this needs to happen here
|
397
|
-
@processor = @processor_class.new
|
398
387
|
end
|
399
388
|
|
400
389
|
def unsubscribe
|
@@ -406,15 +395,23 @@ module ActiveMessaging
|
|
406
395
|
class Destination
|
407
396
|
DEFAULT_PUBLISH_HEADERS = { :persistent=>true }
|
408
397
|
|
409
|
-
attr_accessor :name, :value, :publish_headers, :broker_name
|
398
|
+
attr_accessor :name, :value, :publish_headers, :broker_name, :wildcard
|
410
399
|
|
411
400
|
def initialize(name, value, publish_headers, broker_name)
|
412
401
|
@name, @value, @publish_headers, @broker_name = name, value, publish_headers, broker_name
|
413
402
|
@publish_headers.reverse_merge! DEFAULT_PUBLISH_HEADERS
|
403
|
+
@wildcard = wildcard_match_exp_for(value) if (value =~ /\*/)
|
414
404
|
end
|
415
405
|
|
416
406
|
def to_s
|
417
|
-
"#{broker_name}
|
407
|
+
"<destination: #{broker_name} :#{name}=>'#{value}'>"
|
408
|
+
end
|
409
|
+
|
410
|
+
private
|
411
|
+
|
412
|
+
def wildcard_match_exp_for(destination)
|
413
|
+
exp = destination.to_s.gsub(/[.]/, '\.').gsub(/[*]/, '[^.*]+').gsub(/([>].*$)/, '.*') + '$'
|
414
|
+
Regexp.new(exp)
|
418
415
|
end
|
419
416
|
|
420
417
|
end
|