activemessaging 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -33,6 +33,7 @@ begin
33
33
 
34
34
  # added
35
35
  gemspec.add_dependency('activesupport', '>= 1.0.0')
36
+ gemspec.add_dependency('celluloid')
36
37
 
37
38
  end
38
39
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.0
1
+ 0.10.0
@@ -4,19 +4,19 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{activemessaging}
8
- s.version = "0.9.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 = %q{2011-12-05}
13
- s.description = %q{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 = %q{activemessaging-discuss@googlegroups.com}
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 = %q{http://github.com/kookster/activemessaging}
98
+ s.homepage = "http://github.com/kookster/activemessaging"
97
99
  s.require_paths = ["lib"]
98
- s.rubygems_version = %q{1.4.2}
99
- s.summary = %q{Official activemessaging gem, now hosted on github.com/kookster. (kookster prefix temporary)}
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
 
@@ -1,4 +1,5 @@
1
- require 'active_support'
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 = 1..80
21
- MESSAGE_SIZE = 1..(8 * 1024)
22
- VISIBILITY_TIMEOUT = 0..(24 * 60 * 60)
23
- NUMBER_OF_MESSAGES = 1..255
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
- @subscriptions.delete(queue_name) if @subscriptions[queue_name].count <= 0
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 a single message from any of the subscribed queues
91
- # check each queue once, then sleep for poll_interval
92
- def receive
93
- raise "No subscriptions to receive messages from." if (@subscriptions.nil? || @subscriptions.empty?)
94
- start = @current_subscription
95
- while true
96
- # puts "calling receive..."
97
- @current_subscription = ((@current_subscription < @subscriptions.length-1) ? @current_subscription + 1 : 0)
98
- sleep poll_interval if (@current_subscription == start)
99
- queue_name = @subscriptions.keys.sort[@current_subscription]
100
- queue = queues[queue_name]
101
- subscription = @subscriptions[queue_name]
102
- unless queue.nil?
103
- messages = retrieve_messsages queue, 1, subscription.headers[:visibility_timeout]
104
- return messages[0] unless (messages.nil? or messages.empty? or messages[0].nil?)
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
- end
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 response.get_text("//QueueUrl") unless response.nil?
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 :name, :headers, :count
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
- def receive
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
@@ -46,7 +46,7 @@ module ActiveMessaging
46
46
  @connection.put(message, priority, delay, ttr)
47
47
  end
48
48
 
49
- def receive
49
+ def receive(options={})
50
50
  message = @connection.reserve
51
51
  Beanstalk::Message.new message
52
52
  end
@@ -84,22 +84,24 @@ module ActiveMessaging
84
84
  end
85
85
  producer.send message
86
86
  end
87
-
88
- def receive_any
89
- @consumers.find do |k, c|
90
- message = c.receive(1)
91
- return condition_message(message) unless message.nil?
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 receive queue_name=nil, headers={}
93
+
94
+ def receive_message(queue_name=nil, headers={})
96
95
  if queue_name.nil?
97
- receive_any
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 queue_name, headers
101
+ consumer = subscribe(queue_name, headers)
100
102
  message = consumer.receive(1)
101
- unsubscribe queue_name, headers
102
- condition_message 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
@@ -44,7 +44,7 @@ module ActiveMessaging
44
44
  destination.send Message.new(message_body, nil, message_headers, destination_name)
45
45
  end
46
46
 
47
- def receive
47
+ def receive(options={})
48
48
  destination = @destinations.find do |q|
49
49
  find_subscription(q.name) && !q.empty?
50
50
  end
@@ -49,7 +49,7 @@ module ActiveMessaging
49
49
  end
50
50
 
51
51
  # Receive method needed by a13g
52
- def receive
52
+ def receive(options={})
53
53
  raise "No subscription to receive messages from" if (@queue_names.nil? || @queue_names.empty?)
54
54
  start = @current_queue
55
55
  while true
@@ -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
- cattr_accessor :adapters, :subscriptions, :named_destinations, :filters, :processor_groups, :connections
9
- @@adapters = {}
10
- @@subscriptions = {}
11
- @@named_destinations = {}
12
- @@filters = []
13
- @@connections = {}
14
- @@processor_groups = {}
15
- @@current_processor_group = nil
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
- @@running = true
19
- @@connection_threads = {}
20
- @@guard = Mutex.new
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
- @@connections.each do |name, conn|
32
- @@connection_threads[name] = Thread.start do
33
- while @@running
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
- dispatch Thread.current[:message] if Thread.current[:message]
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 @@running
61
+ while @running
55
62
  trap("TERM", "EXIT")
56
63
  living = false
57
- @@connection_threads.each { |name, thread| living ||= thread.alive? }
58
- @@running = living
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
- @@running = false
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
- @@connection_threads.each do |name, thread|
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 @@connections[broker_name] if @@connections.has_key?(broker_name)
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
- @@connections[broker_name] = adapter_class.new(config)
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
- @@connections.each { |key,connection| connection.disconnect }
138
- @@connections = {}
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
- @@guard.synchronize {
203
- begin
204
- prepare_application
205
- _dispatch(message)
206
- rescue Object => exc
207
- ActiveMessaging.logger.error "Dispatch exception: #{exc}"
208
- ActiveMessaging.logger.error exc.backtrace.join("\n\t")
209
- raise exc
210
- ensure
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
- @@subscriptions["#{proc_name}:#{destination_name}"]= Subscription.new(find_destination(destination_name), processor, headers)
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 && !@@current_processor_group
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
- @@current_processor_group = group_sym
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
- @@current_processor_group
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
- #Try to Load Merb
7
- merb_init_file = File.expand_path(File.dirname(__FILE__)+'/../config/merb_init')
8
- if File.exists? merb_init_file
9
- require File.expand_path(File.dirname(__FILE__)+'/../config/boot')
10
- #need this because of the CWD
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
- # Load Rails
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
- require 'active_support'
21
- require 'activemessaging'
15
+ Rails.logger = Logger.new(STDOUT)
16
+ ActiveMessaging.logger = Rails.logger
22
17
 
23
- # Load ActiveMessaging processors
24
- #ActiveMessaging::load_processors
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
- hash: 59
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
- date: 2011-12-05 00:00:00 -05:00
24
- default_executable:
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
- requirement: &id001 !ruby/object:Gem::Requirement
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
- version_requirements: *id001
42
- 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.
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
- extra_rdoc_files:
49
- - README
50
- files:
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
- hash: 3
142
- segments:
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
- hash: 3
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.4.2
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 prefix temporary)
161
- test_files:
162
- - test/all_tests.rb
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