activemessaging 0.9.0 → 0.10.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/README.md +21 -0
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/activemessaging.gemspec +15 -22
- data/lib/activemessaging.rb +3 -7
- data/lib/activemessaging/adapters/amqp.rb +1 -1
- data/lib/activemessaging/adapters/asqs.rb +71 -24
- data/lib/activemessaging/adapters/base.rb +2 -1
- data/lib/activemessaging/adapters/beanstalk.rb +1 -1
- data/lib/activemessaging/adapters/jms.rb +14 -12
- data/lib/activemessaging/adapters/reliable_msg.rb +1 -1
- data/lib/activemessaging/adapters/stomp.rb +1 -1
- data/lib/activemessaging/adapters/test.rb +1 -1
- data/lib/activemessaging/adapters/wmq.rb +1 -1
- data/lib/activemessaging/gateway.rb +47 -44
- data/lib/activemessaging/threaded_poller.rb +191 -0
- data/lib/generators/active_messaging/install/install_generator.rb +6 -1
- data/lib/generators/active_messaging/install/templates/poller.rb +10 -15
- data/lib/generators/active_messaging/install/templates/threaded_poller +45 -0
- metadata +59 -65
- data/README +0 -28
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Active Messaging
|
2
|
+
|
3
|
+
ActiveMessaging is an attempt to bring the simplicity and elegance of rails development to the world of messaging. Messaging, (or event-driven architecture) is widely used for enterprise integration, with frameworks such as Java's JMS, and products such as ActiveMQ, Tibco, IBM MQSeries, etc.
|
4
|
+
|
5
|
+
ActiveMessaging is a generic framework to ease using messaging, but is not tied to any particular messaging system - in fact, it now has support for Stomp, AMQP, beanstalk, Amazon Simple Queue Service (SQS), JMS (using StompConnect or direct on JRuby), WebSphere MQ, the all-Ruby ReliableMessaging, a mock 'test' adapter, and a 'synch' adapter for use in development that processes calls synchronously (of course) and so requires no broker or additional processes to be running.
|
6
|
+
|
7
|
+
Here's a sample of a processor class that handles incoming messages:
|
8
|
+
|
9
|
+
class HelloWorldProcessor < ActiveMessaging::Processor
|
10
|
+
subscribes_to :hello_world
|
11
|
+
def on_message(message)
|
12
|
+
puts "received: " + message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# Support
|
18
|
+
|
19
|
+
Best bet is the google groups mailing list:
|
20
|
+
|
21
|
+
http://groups.google.com/group/activemessaging-discuss
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.10.0
|
data/activemessaging.gemspec
CHANGED
@@ -4,19 +4,19 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.
|
7
|
+
s.name = "activemessaging"
|
8
|
+
s.version = "0.10.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jon Tirsen", "Andrew Kuklewicz", "Olle Jonsson", "Sylvain Perez", "Cliff Moon", "Uwe Kubosch"]
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
12
|
+
s.date = "2012-07-13"
|
13
|
+
s.description = "ActiveMessaging is an attempt to bring the simplicity and elegance of rails development to the world of messaging. Messaging, (or event-driven architecture) is widely used for enterprise integration, with frameworks such as Java's JMS, and products such as ActiveMQ, Tibco, IBM MQSeries, etc. Now supporting Rails 3 as of version 0.8.0."
|
14
|
+
s.email = "activemessaging-discuss@googlegroups.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
|
-
"README"
|
16
|
+
"README.md"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
|
-
"README",
|
19
|
+
"README.md",
|
20
20
|
"Rakefile",
|
21
21
|
"VERSION",
|
22
22
|
"activemessaging.gemspec",
|
@@ -66,6 +66,7 @@ Gem::Specification.new do |s|
|
|
66
66
|
"lib/activemessaging/processor.rb",
|
67
67
|
"lib/activemessaging/railtie.rb",
|
68
68
|
"lib/activemessaging/test_helper.rb",
|
69
|
+
"lib/activemessaging/threaded_poller.rb",
|
69
70
|
"lib/activemessaging/trace_filter.rb",
|
70
71
|
"lib/generators/active_messaging/install/USAGE",
|
71
72
|
"lib/generators/active_messaging/install/install_generator.rb",
|
@@ -73,6 +74,7 @@ Gem::Specification.new do |s|
|
|
73
74
|
"lib/generators/active_messaging/install/templates/broker.yml",
|
74
75
|
"lib/generators/active_messaging/install/templates/poller",
|
75
76
|
"lib/generators/active_messaging/install/templates/poller.rb",
|
77
|
+
"lib/generators/active_messaging/install/templates/threaded_poller",
|
76
78
|
"lib/generators/active_messaging/processor/USAGE",
|
77
79
|
"lib/generators/active_messaging/processor/processor_generator.rb",
|
78
80
|
"lib/generators/active_messaging/processor/templates/messaging.rb",
|
@@ -93,33 +95,24 @@ Gem::Specification.new do |s|
|
|
93
95
|
"test/test_helper.rb",
|
94
96
|
"test/tracer_test.rb"
|
95
97
|
]
|
96
|
-
s.homepage =
|
98
|
+
s.homepage = "http://github.com/kookster/activemessaging"
|
97
99
|
s.require_paths = ["lib"]
|
98
|
-
s.rubygems_version =
|
99
|
-
s.summary =
|
100
|
-
s.test_files = [
|
101
|
-
"test/all_tests.rb",
|
102
|
-
"test/asqs_test.rb",
|
103
|
-
"test/config_test.rb",
|
104
|
-
"test/filter_test.rb",
|
105
|
-
"test/gateway_test.rb",
|
106
|
-
"test/jms_test.rb",
|
107
|
-
"test/reliable_msg_test.rb",
|
108
|
-
"test/stomp_test.rb",
|
109
|
-
"test/test_helper.rb",
|
110
|
-
"test/tracer_test.rb"
|
111
|
-
]
|
100
|
+
s.rubygems_version = "1.8.22"
|
101
|
+
s.summary = "Official activemessaging gem, now hosted on github.com/kookster. (kookster prefix temporary)"
|
112
102
|
|
113
103
|
if s.respond_to? :specification_version then
|
114
104
|
s.specification_version = 3
|
115
105
|
|
116
106
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
117
107
|
s.add_runtime_dependency(%q<activesupport>, [">= 1.0.0"])
|
108
|
+
s.add_runtime_dependency(%q<celluloid>, [">= 0"])
|
118
109
|
else
|
119
110
|
s.add_dependency(%q<activesupport>, [">= 1.0.0"])
|
111
|
+
s.add_dependency(%q<celluloid>, [">= 0"])
|
120
112
|
end
|
121
113
|
else
|
122
114
|
s.add_dependency(%q<activesupport>, [">= 1.0.0"])
|
115
|
+
s.add_dependency(%q<celluloid>, [">= 0"])
|
123
116
|
end
|
124
117
|
end
|
125
118
|
|
data/lib/activemessaging.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require '
|
1
|
+
require 'logger'
|
2
|
+
require 'active_support/all'
|
2
3
|
require 'ostruct'
|
3
4
|
|
4
5
|
if defined?(Rails::Railtie)
|
@@ -45,6 +46,7 @@ module ActiveMessaging
|
|
45
46
|
def self.load_extensions
|
46
47
|
require 'logger'
|
47
48
|
require 'activemessaging/gateway'
|
49
|
+
require 'activemessaging/threaded_poller'
|
48
50
|
require 'activemessaging/adapter'
|
49
51
|
require 'activemessaging/message_sender'
|
50
52
|
require 'activemessaging/processor'
|
@@ -66,8 +68,6 @@ module ActiveMessaging
|
|
66
68
|
path = File.expand_path("#{app_root}/config/messaging.rb")
|
67
69
|
begin
|
68
70
|
load path
|
69
|
-
rescue MissingSourceFile
|
70
|
-
logger.error "ActiveMessaging: no '#{path}' file to load"
|
71
71
|
rescue
|
72
72
|
raise $!, " ActiveMessaging: problems trying to load '#{path}': \n\t#{$!.message}"
|
73
73
|
end
|
@@ -126,7 +126,3 @@ module ActiveMessaging
|
|
126
126
|
end
|
127
127
|
|
128
128
|
end
|
129
|
-
|
130
|
-
if !defined?(Rails::Railtie)
|
131
|
-
ActiveMessaging.load_activemessaging
|
132
|
-
end
|
@@ -79,7 +79,7 @@ module ActiveMessaging
|
|
79
79
|
client.server.send_frame(::Carrot::AMQP::Protocol::Basic::Reject.new(:delivery_tag => message.headers[:delivery_tag]))
|
80
80
|
end
|
81
81
|
|
82
|
-
def receive
|
82
|
+
def receive(options={})
|
83
83
|
while true
|
84
84
|
message = queue.pop(:ack => true)
|
85
85
|
unless message.nil?
|
@@ -17,10 +17,10 @@ module ActiveMessaging
|
|
17
17
|
class Connection < ActiveMessaging::Adapters::BaseConnection
|
18
18
|
register :asqs
|
19
19
|
|
20
|
-
QUEUE_NAME_LENGTH
|
21
|
-
MESSAGE_SIZE
|
22
|
-
VISIBILITY_TIMEOUT
|
23
|
-
NUMBER_OF_MESSAGES
|
20
|
+
QUEUE_NAME_LENGTH = 1..80
|
21
|
+
MESSAGE_SIZE = 1..(8 * 1024)
|
22
|
+
VISIBILITY_TIMEOUT = 0..(24 * 60 * 60)
|
23
|
+
NUMBER_OF_MESSAGES = 1..255
|
24
24
|
GET_QUEUE_ATTRIBUTES = ['All', 'ApproximateNumberOfMessages', 'VisibilityTimeout']
|
25
25
|
SET_QUEUE_ATTRIBUTES = ['VisibilityTimeout']
|
26
26
|
|
@@ -50,6 +50,7 @@ module ActiveMessaging
|
|
50
50
|
|
51
51
|
#initialize the subscriptions and queues
|
52
52
|
@subscriptions = {}
|
53
|
+
@queues_by_priority = {}
|
53
54
|
@current_subscription = 0
|
54
55
|
queues
|
55
56
|
end
|
@@ -69,6 +70,10 @@ module ActiveMessaging
|
|
69
70
|
else
|
70
71
|
@subscriptions[queue.name] = Subscription.new(queue.name, message_headers)
|
71
72
|
end
|
73
|
+
priority = @subscriptions[queue.name].priority
|
74
|
+
|
75
|
+
@queues_by_priority[priority] = [] unless @queues_by_priority.has_key?(priority)
|
76
|
+
@queues_by_priority[priority] << queue.name unless @queues_by_priority[priority].include?(queue.name)
|
72
77
|
end
|
73
78
|
|
74
79
|
# queue_name string, headers hash
|
@@ -76,7 +81,10 @@ module ActiveMessaging
|
|
76
81
|
def unsubscribe queue_name, message_headers={}
|
77
82
|
if @subscriptions[queue_name]
|
78
83
|
@subscriptions[queue_name].remove
|
79
|
-
|
84
|
+
if @subscriptions[queue_name].count <= 0
|
85
|
+
sub = @subscriptions.delete(queue_name)
|
86
|
+
@queues_by_priority[sub.priority].delete(queue_name)
|
87
|
+
end
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
@@ -87,24 +95,60 @@ module ActiveMessaging
|
|
87
95
|
send_messsage queue, message_body
|
88
96
|
end
|
89
97
|
|
90
|
-
# receive
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
98
|
+
# new receive respects priorities
|
99
|
+
def receive(options={})
|
100
|
+
message = nil
|
101
|
+
|
102
|
+
only_priorities = options[:priorities]
|
103
|
+
|
104
|
+
# loop through the priorities
|
105
|
+
@queues_by_priority.keys.sort.each do |priority|
|
106
|
+
|
107
|
+
# skip this priority if there is a list, and it is not in the list
|
108
|
+
next if only_priorities && !only_priorities.include?(priority.to_i)
|
109
|
+
|
110
|
+
# puts " - priority: #{priority}"
|
111
|
+
# loop through queues for the priority in random order each time
|
112
|
+
@queues_by_priority[priority].shuffle.each do |queue_name|
|
113
|
+
# puts " - queue_name: #{queue_name}"
|
114
|
+
queue = queues[queue_name]
|
115
|
+
subscription = @subscriptions[queue_name]
|
116
|
+
|
117
|
+
next if queue.nil? || subscription.nil?
|
118
|
+
messages = retrieve_messsages(queue, 1, subscription.headers[:visibility_timeout])
|
119
|
+
|
120
|
+
if (messages && !messages.empty?)
|
121
|
+
message = messages[0]
|
122
|
+
end
|
123
|
+
|
124
|
+
break if message
|
105
125
|
end
|
126
|
+
|
127
|
+
break if message
|
106
128
|
end
|
107
|
-
|
129
|
+
|
130
|
+
# puts " - message: #{message}"
|
131
|
+
message
|
132
|
+
end
|
133
|
+
|
134
|
+
# # receive a single message from any of the subscribed queues
|
135
|
+
# # check each queue once, then sleep for poll_interval
|
136
|
+
# def receive
|
137
|
+
# raise "No subscriptions to receive messages from." if (@subscriptions.nil? || @subscriptions.empty?)
|
138
|
+
# start = @current_subscription
|
139
|
+
# while true
|
140
|
+
# # puts "calling receive..."
|
141
|
+
# @current_subscription = ((@current_subscription < @subscriptions.length-1) ? @current_subscription + 1 : 0)
|
142
|
+
# sleep poll_interval if (@current_subscription == start)
|
143
|
+
# queue_name = @subscriptions.keys.sort[@current_subscription]
|
144
|
+
# queue = queues[queue_name]
|
145
|
+
# subscription = @subscriptions[queue_name]
|
146
|
+
# unless queue.nil?
|
147
|
+
# messages = retrieve_messsages queue, 1, subscription.headers[:visibility_timeout]
|
148
|
+
# return messages[0] unless (messages.nil? or messages.empty? or messages[0].nil?)
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
# end
|
108
152
|
|
109
153
|
def received message, headers={}
|
110
154
|
begin
|
@@ -125,7 +169,7 @@ module ActiveMessaging
|
|
125
169
|
def create_queue(name)
|
126
170
|
validate_new_queue name
|
127
171
|
response = make_request('CreateQueue', nil, {'QueueName'=>name})
|
128
|
-
add_queue
|
172
|
+
add_queue(response.get_text("//QueueUrl")) unless response.nil?
|
129
173
|
end
|
130
174
|
|
131
175
|
def delete_queue queue
|
@@ -264,7 +308,9 @@ module ActiveMessaging
|
|
264
308
|
|
265
309
|
def get_or_create_queue queue_name
|
266
310
|
qs = queues
|
267
|
-
qs.has_key?(queue_name) ? qs[queue_name] : create_queue(queue_name)
|
311
|
+
q = qs.has_key?(queue_name) ? qs[queue_name] : create_queue(queue_name)
|
312
|
+
raise "could not get or create queue: #{queue_name}" unless q
|
313
|
+
q
|
268
314
|
end
|
269
315
|
|
270
316
|
def queues
|
@@ -359,9 +405,10 @@ module ActiveMessaging
|
|
359
405
|
end
|
360
406
|
|
361
407
|
class Subscription
|
362
|
-
attr_accessor :
|
408
|
+
attr_accessor :destination, :headers, :count, :priority
|
363
409
|
|
364
410
|
def initialize(destination, headers={}, count=1)
|
411
|
+
@priority = headers.delete(:priority) || 1001
|
365
412
|
@destination, @headers, @count = destination, headers, count
|
366
413
|
end
|
367
414
|
|
@@ -39,7 +39,8 @@ module ActiveMessaging
|
|
39
39
|
|
40
40
|
# receive a single message from any of the subscribed destinations
|
41
41
|
# check each destination once, then sleep for poll_interval
|
42
|
-
|
42
|
+
# adding options,optionally, so a poller can get certain messages (e.g. by priority)
|
43
|
+
def receive(options={})
|
43
44
|
end
|
44
45
|
|
45
46
|
# called after a message is successfully received and processed
|
@@ -84,22 +84,24 @@ module ActiveMessaging
|
|
84
84
|
end
|
85
85
|
producer.send message
|
86
86
|
end
|
87
|
-
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
87
|
+
|
88
|
+
def receive(options={})
|
89
|
+
queue_name = options[:queue_name]
|
90
|
+
headers = options[:headers] || {}
|
91
|
+
receive_message(queue_name, headers)
|
93
92
|
end
|
94
|
-
|
95
|
-
def
|
93
|
+
|
94
|
+
def receive_message(queue_name=nil, headers={})
|
96
95
|
if queue_name.nil?
|
97
|
-
|
96
|
+
@consumers.find do |k, c|
|
97
|
+
message = c.receive(1)
|
98
|
+
return condition_message(message) unless message.nil?
|
99
|
+
end
|
98
100
|
else
|
99
|
-
consumer = subscribe
|
101
|
+
consumer = subscribe(queue_name, headers)
|
100
102
|
message = consumer.receive(1)
|
101
|
-
unsubscribe
|
102
|
-
condition_message
|
103
|
+
unsubscribe(queue_name, headers)
|
104
|
+
condition_message(message)
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
@@ -89,7 +89,7 @@ module ActiveMessaging
|
|
89
89
|
|
90
90
|
# receive a single message from any of the subscribed destinations
|
91
91
|
# check each destination once, then sleep for poll_interval
|
92
|
-
def receive
|
92
|
+
def receive(options={})
|
93
93
|
|
94
94
|
raise "No subscriptions to receive messages from." if (subscriptions.nil? || subscriptions.empty?)
|
95
95
|
start = current_subscription
|
@@ -73,7 +73,7 @@ module ActiveMessaging
|
|
73
73
|
|
74
74
|
# receive a single message from any of the subscribed destinations
|
75
75
|
# check each destination once, then sleep for poll_interval
|
76
|
-
def receive
|
76
|
+
def receive(options={})
|
77
77
|
m = @stomp_connection.receive
|
78
78
|
Message.new(m) if m
|
79
79
|
end
|
@@ -1,25 +1,28 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'ostruct'
|
3
3
|
require 'erb'
|
4
|
+
require 'thread'
|
4
5
|
|
5
6
|
module ActiveMessaging
|
6
7
|
|
7
8
|
class Gateway
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
|
10
|
+
@adapters = {}
|
11
|
+
@subscriptions = {}
|
12
|
+
@named_destinations = {}
|
13
|
+
@filters = []
|
14
|
+
@connections = {}
|
15
|
+
@processor_groups = {}
|
16
|
+
@current_processor_group = nil
|
16
17
|
|
17
18
|
# these are used to manage the running connection threads
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
class <<self
|
19
|
+
@running = true
|
20
|
+
@connection_threads = {}
|
21
|
+
@guard = Mutex.new
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
attr_accessor :adapters, :subscriptions, :named_destinations, :filters, :processor_groups, :connections
|
23
26
|
|
24
27
|
# Starts up an message listener to start polling for messages on each configured connection, and dispatching processing
|
25
28
|
def start
|
@@ -28,9 +31,9 @@ module ActiveMessaging
|
|
28
31
|
subscribe
|
29
32
|
|
30
33
|
# for each connection, start a thread
|
31
|
-
|
32
|
-
|
33
|
-
while
|
34
|
+
@connections.each do |name, conn|
|
35
|
+
@connection_threads[name] = Thread.start do
|
36
|
+
while @running
|
34
37
|
begin
|
35
38
|
Thread.current[:message] = nil
|
36
39
|
Thread.current[:message] = conn.receive
|
@@ -42,7 +45,11 @@ module ActiveMessaging
|
|
42
45
|
rescue Object=>exception
|
43
46
|
ActiveMessaging.logger.error "ActiveMessaging: thread[#{name}]: Exception from connection.receive: #{exception.message}\n" + exception.backtrace.join("\n\t")
|
44
47
|
ensure
|
45
|
-
|
48
|
+
if Thread.current[:message]
|
49
|
+
@guard.synchronize {
|
50
|
+
dispatch Thread.current[:message]
|
51
|
+
}
|
52
|
+
end
|
46
53
|
Thread.current[:message] = nil
|
47
54
|
end
|
48
55
|
Thread.pass
|
@@ -51,11 +58,11 @@ module ActiveMessaging
|
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
|
-
while
|
61
|
+
while @running
|
55
62
|
trap("TERM", "EXIT")
|
56
63
|
living = false
|
57
|
-
|
58
|
-
|
64
|
+
@connection_threads.each { |name, thread| living ||= thread.alive? }
|
65
|
+
@running = living
|
59
66
|
sleep 1
|
60
67
|
end
|
61
68
|
ActiveMessaging.logger.error "All connection threads have died..."
|
@@ -72,14 +79,14 @@ module ActiveMessaging
|
|
72
79
|
|
73
80
|
def stop
|
74
81
|
# first tell the threads to stop their looping, so they'll stop when next complete a receive/dispatch cycle
|
75
|
-
|
82
|
+
@running = false
|
76
83
|
|
77
84
|
# if they are dispatching (i.e. !thread[:message].nil?), wait for them to finish
|
78
85
|
# if they are receiving (i.e. thread[:message].nil?), stop them by raising exception
|
79
86
|
dispatching = true
|
80
87
|
while dispatching
|
81
88
|
dispatching = false
|
82
|
-
|
89
|
+
@connection_threads.each do |name, thread|
|
83
90
|
if thread[:message]
|
84
91
|
dispatching = true
|
85
92
|
# if thread got killed, but dispatch not done, try it again
|
@@ -109,15 +116,15 @@ module ActiveMessaging
|
|
109
116
|
end
|
110
117
|
|
111
118
|
def connection broker_name='default'
|
112
|
-
return
|
119
|
+
return @connections[broker_name] if @connections.has_key?(broker_name)
|
113
120
|
config = load_connection_configuration(broker_name)
|
114
121
|
adapter_class = Gateway.adapters[config[:adapter]]
|
115
122
|
raise "Unknown messaging adapter #{config[:adapter].inspect}!" if adapter_class.nil?
|
116
|
-
|
123
|
+
@connections[broker_name] = adapter_class.new(config)
|
117
124
|
end
|
118
125
|
|
119
126
|
def register_adapter adapter_name, adapter_class
|
120
|
-
adapters[adapter_name] = adapter_class
|
127
|
+
Gateway.adapters[adapter_name] = adapter_class
|
121
128
|
end
|
122
129
|
|
123
130
|
def filter filter, options = {}
|
@@ -134,8 +141,8 @@ module ActiveMessaging
|
|
134
141
|
end
|
135
142
|
|
136
143
|
def disconnect
|
137
|
-
|
138
|
-
|
144
|
+
@connections.each { |key,connection| connection.disconnect }
|
145
|
+
@connections = {}
|
139
146
|
end
|
140
147
|
|
141
148
|
def execute_filter_chain(direction, message, details={})
|
@@ -199,19 +206,15 @@ module ActiveMessaging
|
|
199
206
|
end
|
200
207
|
|
201
208
|
def dispatch(message)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
ActiveMessaging.logger.flush rescue nil
|
212
|
-
reset_application
|
213
|
-
end
|
214
|
-
}
|
209
|
+
prepare_application
|
210
|
+
_dispatch(message)
|
211
|
+
rescue Object => exc
|
212
|
+
ActiveMessaging.logger.error "Dispatch exception: #{exc}"
|
213
|
+
ActiveMessaging.logger.error exc.backtrace.join("\n\t")
|
214
|
+
raise exc
|
215
|
+
ensure
|
216
|
+
ActiveMessaging.logger.flush rescue nil
|
217
|
+
reset_application
|
215
218
|
end
|
216
219
|
|
217
220
|
def _dispatch(message)
|
@@ -279,7 +282,7 @@ module ActiveMessaging
|
|
279
282
|
proc_name = processor.name.underscore
|
280
283
|
proc_sym = processor.name.underscore.to_sym
|
281
284
|
if (!current_processor_group || processor_groups[current_processor_group].include?(proc_sym))
|
282
|
-
|
285
|
+
@subscriptions["#{proc_name}:#{destination_name}"]= Subscription.new(find_destination(destination_name), processor, headers)
|
283
286
|
end
|
284
287
|
end
|
285
288
|
|
@@ -335,13 +338,13 @@ module ActiveMessaging
|
|
335
338
|
end
|
336
339
|
|
337
340
|
def current_processor_group
|
338
|
-
if ARGV.length > 0 &&
|
341
|
+
if ARGV.length > 0 && !@current_processor_group
|
339
342
|
ARGV.each {|arg|
|
340
343
|
pair = arg.split('=')
|
341
344
|
if pair[0] == 'process-group'
|
342
345
|
group_sym = pair[1].to_sym
|
343
346
|
if processor_groups.has_key? group_sym
|
344
|
-
|
347
|
+
@current_processor_group = group_sym
|
345
348
|
else
|
346
349
|
ActiveMessaging.logger.error "Unrecognized process-group."
|
347
350
|
ActiveMessaging.logger.error "You specified process-group #{pair[1]}, make sure this is specified in config/messaging.rb"
|
@@ -353,7 +356,7 @@ module ActiveMessaging
|
|
353
356
|
end
|
354
357
|
}
|
355
358
|
end
|
356
|
-
|
359
|
+
@current_processor_group
|
357
360
|
end
|
358
361
|
|
359
362
|
def load_connection_configuration(label='default')
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# still working on prioritized worker requests
|
2
|
+
|
3
|
+
|
4
|
+
# This owes no small debt to sidekiq for showing how to use celluloid for polling for messages.
|
5
|
+
# https://github.com/mperham/sidekiq/blob/poller/lib/sidekiq/manager.rb
|
6
|
+
|
7
|
+
require 'celluloid'
|
8
|
+
|
9
|
+
module ActiveMessaging
|
10
|
+
|
11
|
+
class ThreadedPoller
|
12
|
+
|
13
|
+
include Celluloid
|
14
|
+
|
15
|
+
# traps when any worker dies
|
16
|
+
trap_exit :died
|
17
|
+
|
18
|
+
attr_accessor :configuration, :receiver, :connection, :workers, :busy, :running
|
19
|
+
|
20
|
+
#
|
21
|
+
# connection is a string, name of the connection from broker.yml to use for this threaded poller instance
|
22
|
+
#
|
23
|
+
# configuration is a list of hashes
|
24
|
+
# each has describes a group of worker threads
|
25
|
+
# for each group, define what priorities those workers will process
|
26
|
+
# [
|
27
|
+
# {
|
28
|
+
# :pool_size => 1 # number of workers of this type
|
29
|
+
# :priorities => [1,2,3] # what message priorities this thread will process
|
30
|
+
# }
|
31
|
+
# ]
|
32
|
+
#
|
33
|
+
def initialize(connection='default', configuration={})
|
34
|
+
# default config is a pool size of 3 worker threads
|
35
|
+
self.configuration = configuration || [{:pool_size => 3}]
|
36
|
+
self.connection = connection
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
logger.info "ActiveMessaging::ThreadedPoller start"
|
41
|
+
|
42
|
+
# these are workers ready to use
|
43
|
+
self.workers = []
|
44
|
+
|
45
|
+
# these are workers already working
|
46
|
+
self.busy = []
|
47
|
+
|
48
|
+
# this indicates if we are running or not, helps threads to stop gracefully
|
49
|
+
self.running = true
|
50
|
+
|
51
|
+
# subscribe will create the connections based on subscriptions in processsors
|
52
|
+
# (you can't find or use the connection until it is created by calling this)
|
53
|
+
ActiveMessaging::Gateway.subscribe
|
54
|
+
|
55
|
+
# create a message receiver actor, ony need one, using connection
|
56
|
+
receiver_connection = ActiveMessaging::Gateway.connection(connection)
|
57
|
+
self.receiver = MessageReceiver.new(current_actor, receiver_connection)
|
58
|
+
|
59
|
+
# start the workers based on the config
|
60
|
+
configuration.each do |c|
|
61
|
+
(c[:pool_size] || 1).times{ self.workers << Worker.new_link(current_actor, c) }
|
62
|
+
end
|
63
|
+
|
64
|
+
# once all workers are created, start them up
|
65
|
+
self.workers.each{|worker| receive(worker)}
|
66
|
+
|
67
|
+
# in debug level, log info about workers every 10 seconds
|
68
|
+
log_status
|
69
|
+
end
|
70
|
+
|
71
|
+
def stop
|
72
|
+
logger.info "ActiveMessaging::ThreadedPoller stop"
|
73
|
+
# indicates to all busy workers not to pick up another messages, but does not interrupt
|
74
|
+
# also indicates to the message receiver to stop getting more messages
|
75
|
+
self.running = false
|
76
|
+
|
77
|
+
# tell each waiting worker to shut down. Running ones will be allowed to finish
|
78
|
+
workers.each { |w| w.terminate if w.alive? }
|
79
|
+
end
|
80
|
+
|
81
|
+
# recursive method, uses celluloid 'after' to keep calling
|
82
|
+
def log_status
|
83
|
+
return unless logger.debug?
|
84
|
+
logger.debug("ActiveMessaging::ThreadedPoller: conn:#{connection}, #{workers.count}, #{busy.count}, #{running}")
|
85
|
+
after(10){ log_status }
|
86
|
+
end
|
87
|
+
|
88
|
+
def receive(worker)
|
89
|
+
receiver.receive!(worker) if (receiver && running && worker)
|
90
|
+
end
|
91
|
+
|
92
|
+
def dispatch(message, worker)
|
93
|
+
workers.delete(worker)
|
94
|
+
busy << worker
|
95
|
+
worker.execute!(message)
|
96
|
+
end
|
97
|
+
|
98
|
+
def executed(worker)
|
99
|
+
busy.delete(worker)
|
100
|
+
|
101
|
+
if running
|
102
|
+
workers << worker
|
103
|
+
receive(worker)
|
104
|
+
else
|
105
|
+
worker.terminate if worker.alive?
|
106
|
+
if busy.empty?
|
107
|
+
logger.info "all executed: signal stopped"
|
108
|
+
self.terminate if alive?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def died(worker, reason)
|
114
|
+
logger.info "uh oh, #{worker.inspect} died because of #{reason.class}"
|
115
|
+
busy.delete(worker)
|
116
|
+
|
117
|
+
if running
|
118
|
+
worker = Worker.new_link(current_actor)
|
119
|
+
workers << worker
|
120
|
+
receive(worker)
|
121
|
+
else
|
122
|
+
logger.info "check to see if busy is empty: #{busy.inspect}"
|
123
|
+
if busy.empty?
|
124
|
+
logger.info "all died: signal stopped"
|
125
|
+
after(0){ self.terminate } if alive?
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def stopped?
|
131
|
+
!alive? || (!running && busy.empty?)
|
132
|
+
end
|
133
|
+
|
134
|
+
def logger; ActiveMessaging.logger; end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
class MessageReceiver
|
139
|
+
include Celluloid
|
140
|
+
|
141
|
+
attr_accessor :poller, :connection, :pause
|
142
|
+
|
143
|
+
def initialize(poller, connection, pause=1)
|
144
|
+
logger.debug("MessageReceiver initialize: poller:#{poller}, connection:#{connection}, pause:#{pause}")
|
145
|
+
|
146
|
+
raise "No connection found for '#{poller.connection}'" unless connection
|
147
|
+
|
148
|
+
self.poller = poller
|
149
|
+
self.connection = connection
|
150
|
+
self.pause = pause
|
151
|
+
end
|
152
|
+
|
153
|
+
def receive(worker)
|
154
|
+
return unless poller.running
|
155
|
+
|
156
|
+
message = self.connection.receive(worker.options)
|
157
|
+
|
158
|
+
if message
|
159
|
+
logger.debug("ActiveMessaging::MessageReceiver.receive: message:'#{message.inspect}'")
|
160
|
+
poller.dispatch!(message, worker)
|
161
|
+
else
|
162
|
+
self.terminate if !poller.running && alive?
|
163
|
+
logger.debug("ActiveMessaging::MessageReceiver.receive: no message, retry in #{pause} sec")
|
164
|
+
after(pause) { receive(worker) }
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def logger; ActiveMessaging.logger; end
|
170
|
+
end
|
171
|
+
|
172
|
+
class Worker
|
173
|
+
include Celluloid
|
174
|
+
|
175
|
+
attr_accessor :poller, :options
|
176
|
+
|
177
|
+
def initialize(poller, options)
|
178
|
+
self.poller = poller
|
179
|
+
self.options = options
|
180
|
+
end
|
181
|
+
|
182
|
+
def execute(message)
|
183
|
+
ActiveMessaging::Gateway.dispatch(message)
|
184
|
+
poller.executed!(current_actor)
|
185
|
+
end
|
186
|
+
|
187
|
+
def logger; ActiveMessaging.logger; end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -12,7 +12,12 @@ module ActiveMessaging
|
|
12
12
|
template "poller", "script/#{poller_name}"
|
13
13
|
chmod("script/#{poller_name}", 0755)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
|
+
def copy_threaded_poller
|
17
|
+
template "threaded_poller", "script/threaded_#{poller_name}"
|
18
|
+
chmod("script/threaded_#{poller_name}", 0755)
|
19
|
+
end
|
20
|
+
|
16
21
|
def copy_poller_rb
|
17
22
|
copy_file "poller.rb", "lib/#{poller_name}.rb"
|
18
23
|
end
|
@@ -3,25 +3,20 @@
|
|
3
3
|
STDOUT.sync = true; STDOUT.flush
|
4
4
|
STDERR.sync = true; STDERR.flush
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Merb.root = MERB_ROOT
|
12
|
-
require merb_init_file
|
6
|
+
app_root = ENV['APP_ROOT'] || File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
7
|
+
application_file = File.join(app_root, 'config', 'environment.rb')
|
8
|
+
|
9
|
+
if File.exist?(application_file)
|
10
|
+
load application_file
|
13
11
|
else
|
14
|
-
#
|
15
|
-
RAILS_ROOT=File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
16
|
-
require File.join(RAILS_ROOT, 'config', 'boot')
|
17
|
-
require File.join(RAILS_ROOT, 'config', 'environment')
|
12
|
+
raise "#{application_file} does not exist!"
|
18
13
|
end
|
19
14
|
|
20
|
-
|
21
|
-
|
15
|
+
Rails.logger = Logger.new(STDOUT)
|
16
|
+
ActiveMessaging.logger = Rails.logger
|
22
17
|
|
23
|
-
# Load ActiveMessaging
|
24
|
-
|
18
|
+
# Load ActiveMessaging
|
19
|
+
ActiveMessaging::load_processors
|
25
20
|
|
26
21
|
# Start it up!
|
27
22
|
ActiveMessaging::start
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# The threaded poller will use celluloid to generate many threads for a single poller process to handle messages
|
4
|
+
# Yes, I absolutely looked at the sidekiq manager code, and this code is very very similar
|
5
|
+
# This script also shows how to use the poller without loading Rails, though it loads the app lib directory
|
6
|
+
# this is tested on 1.9.3, and rails 3+
|
7
|
+
|
8
|
+
# Make sure stdout and stderr write out without delay for using with daemon like scripts
|
9
|
+
STDOUT.sync = true; STDOUT.flush
|
10
|
+
STDERR.sync = true; STDERR.flush
|
11
|
+
|
12
|
+
# we're not going to load rails, but activemessaging does expect there to be an app root
|
13
|
+
# this goes back to when it was used in both rails or merb, but work with just not loading a framework
|
14
|
+
app_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
15
|
+
ENV['APP_ROOT'] ||= app_root
|
16
|
+
|
17
|
+
# minimal requirements, other requires are in the lib
|
18
|
+
require 'rubygems'
|
19
|
+
require 'bundler/setup'
|
20
|
+
|
21
|
+
# load the app lib directory
|
22
|
+
lib_path = File.dirname("#{app_root}/lib")
|
23
|
+
$:.unshift(lib_path)
|
24
|
+
Dir["#{lib_path}/**/*.rb"].each {|file| require file unless file.match(/poller\.rb/) }
|
25
|
+
|
26
|
+
# require and load activemessaging
|
27
|
+
require 'activemessaging'
|
28
|
+
ActiveMessaging.load_activemessaging
|
29
|
+
|
30
|
+
# configure the connection (there can be multiple defined in broker.yml) and number of threads
|
31
|
+
connection_name = 'default'
|
32
|
+
configuration = [{:pool_size => 3}]
|
33
|
+
|
34
|
+
# start it up!
|
35
|
+
begin
|
36
|
+
trap("TERM", "EXIT")
|
37
|
+
@poller = ActiveMessaging::ThreadedPoller.new(connection_name, configuration)
|
38
|
+
@poller.start!
|
39
|
+
sleep
|
40
|
+
rescue Interrupt
|
41
|
+
puts "-- Interrupt --"
|
42
|
+
@poller.stop!
|
43
|
+
@poller.wait(:shutdown)
|
44
|
+
exit(0)
|
45
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: activemessaging
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.10.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 9
|
9
|
-
- 0
|
10
|
-
version: 0.9.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Jon Tirsen
|
14
9
|
- Andrew Kuklewicz
|
15
10
|
- Olle Jonsson
|
@@ -19,36 +14,52 @@ authors:
|
|
19
14
|
autorequire:
|
20
15
|
bindir: bin
|
21
16
|
cert_chain: []
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
dependencies:
|
26
|
-
- !ruby/object:Gem::Dependency
|
17
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
27
20
|
name: activesupport
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
27
|
+
type: :runtime
|
28
28
|
prerelease: false
|
29
|
-
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
|
-
requirements:
|
32
|
-
- -
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
hash: 23
|
35
|
-
segments:
|
36
|
-
- 1
|
37
|
-
- 0
|
38
|
-
- 0
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
39
34
|
version: 1.0.0
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: celluloid
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
40
43
|
type: :runtime
|
41
|
-
|
42
|
-
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
description: ActiveMessaging is an attempt to bring the simplicity and elegance of
|
52
|
+
rails development to the world of messaging. Messaging, (or event-driven architecture)
|
53
|
+
is widely used for enterprise integration, with frameworks such as Java's JMS, and
|
54
|
+
products such as ActiveMQ, Tibco, IBM MQSeries, etc. Now supporting Rails 3 as of
|
55
|
+
version 0.8.0.
|
43
56
|
email: activemessaging-discuss@googlegroups.com
|
44
57
|
executables: []
|
45
|
-
|
46
58
|
extensions: []
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
- README
|
59
|
+
extra_rdoc_files:
|
60
|
+
- README.md
|
61
|
+
files:
|
62
|
+
- README.md
|
52
63
|
- Rakefile
|
53
64
|
- VERSION
|
54
65
|
- activemessaging.gemspec
|
@@ -98,6 +109,7 @@ files:
|
|
98
109
|
- lib/activemessaging/processor.rb
|
99
110
|
- lib/activemessaging/railtie.rb
|
100
111
|
- lib/activemessaging/test_helper.rb
|
112
|
+
- lib/activemessaging/threaded_poller.rb
|
101
113
|
- lib/activemessaging/trace_filter.rb
|
102
114
|
- lib/generators/active_messaging/install/USAGE
|
103
115
|
- lib/generators/active_messaging/install/install_generator.rb
|
@@ -105,6 +117,7 @@ files:
|
|
105
117
|
- lib/generators/active_messaging/install/templates/broker.yml
|
106
118
|
- lib/generators/active_messaging/install/templates/poller
|
107
119
|
- lib/generators/active_messaging/install/templates/poller.rb
|
120
|
+
- lib/generators/active_messaging/install/templates/threaded_poller
|
108
121
|
- lib/generators/active_messaging/processor/USAGE
|
109
122
|
- lib/generators/active_messaging/processor/processor_generator.rb
|
110
123
|
- lib/generators/active_messaging/processor/templates/messaging.rb
|
@@ -124,48 +137,29 @@ files:
|
|
124
137
|
- test/stomp_test.rb
|
125
138
|
- test/test_helper.rb
|
126
139
|
- test/tracer_test.rb
|
127
|
-
has_rdoc: true
|
128
140
|
homepage: http://github.com/kookster/activemessaging
|
129
141
|
licenses: []
|
130
|
-
|
131
142
|
post_install_message:
|
132
143
|
rdoc_options: []
|
133
|
-
|
134
|
-
require_paths:
|
144
|
+
require_paths:
|
135
145
|
- lib
|
136
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
147
|
none: false
|
138
|
-
requirements:
|
139
|
-
- -
|
140
|
-
- !ruby/object:Gem::Version
|
141
|
-
|
142
|
-
|
143
|
-
- 0
|
144
|
-
version: "0"
|
145
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
153
|
none: false
|
147
|
-
requirements:
|
148
|
-
- -
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
|
151
|
-
segments:
|
152
|
-
- 0
|
153
|
-
version: "0"
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
154
158
|
requirements: []
|
155
|
-
|
156
159
|
rubyforge_project:
|
157
|
-
rubygems_version: 1.
|
160
|
+
rubygems_version: 1.8.22
|
158
161
|
signing_key:
|
159
162
|
specification_version: 3
|
160
|
-
summary: Official activemessaging gem, now hosted on github.com/kookster. (kookster
|
161
|
-
|
162
|
-
|
163
|
-
- test/asqs_test.rb
|
164
|
-
- test/config_test.rb
|
165
|
-
- test/filter_test.rb
|
166
|
-
- test/gateway_test.rb
|
167
|
-
- test/jms_test.rb
|
168
|
-
- test/reliable_msg_test.rb
|
169
|
-
- test/stomp_test.rb
|
170
|
-
- test/test_helper.rb
|
171
|
-
- test/tracer_test.rb
|
163
|
+
summary: Official activemessaging gem, now hosted on github.com/kookster. (kookster
|
164
|
+
prefix temporary)
|
165
|
+
test_files: []
|
data/README
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
ActiveMessaging is an attempt to bring the simplicity and elegance of rails development to the world of messaging. Messaging, (or event-driven architecture) is widely used for enterprise integration, with frameworks such as Java's JMS, and products such as ActiveMQ, Tibco, IBM MQSeries, etc.
|
2
|
-
|
3
|
-
ActiveMessaging is a generic framework to ease using messaging, but is not tied to any particular messaging system - in fact, it now has support for Stomp, Amazon Simple Queue Service (SQS), Beanstalk, JMS (using StompConnect or [JMSWithJRuby direct on JRuby]), WebSphere MQ, and the all-Ruby ReliableMessaging.
|
4
|
-
|
5
|
-
Here's a sample of a processor class that handles incoming messages:
|
6
|
-
|
7
|
-
class HelloWorldProcessor < ActiveMessaging::Processor
|
8
|
-
|
9
|
-
subscribes_to :hello_world
|
10
|
-
|
11
|
-
def on_message(message)
|
12
|
-
puts "received: " + message
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
h1. Support
|
19
|
-
|
20
|
-
Best bet is the google groups mailing list:
|
21
|
-
|
22
|
-
http://groups.google.com/group/activemessaging-discuss
|
23
|
-
|
24
|
-
h1. Fork Details
|
25
|
-
|
26
|
-
This fork adds the ability to specify a dead letter queue prefix so instead of all messages going to 1 dead letter queue, each message will go to a dead letter queue that is based on the deadLetterQueuePrefix configuration property and the originating queue.
|
27
|
-
|
28
|
-
For example, if you specific a deadLetterQueuePrefix of "DLQ." in your broker.yml and the originating queue is /queue/myqueue then the dead letter queue will be /queue/DLQ.myqueue
|