modern_times 0.1.2 → 0.2.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.
Files changed (64) hide show
  1. data/README.rdoc +24 -12
  2. data/Rakefile +2 -2
  3. data/VERSION +1 -1
  4. data/examples/README +4 -0
  5. data/examples/jms.yml +9 -0
  6. data/examples/requestor/README +4 -2
  7. data/examples/requestor/manager.rb +3 -2
  8. data/examples/requestor/request.rb +5 -4
  9. data/examples/requestor/reverse_echo_worker.rb +3 -2
  10. data/examples/simple/README +7 -4
  11. data/examples/simple/bar_worker.rb +4 -1
  12. data/examples/simple/baz_worker.rb +4 -3
  13. data/examples/simple/manager.rb +3 -2
  14. data/examples/simple/publish.rb +6 -5
  15. data/lib/modern_times.rb +20 -2
  16. data/lib/modern_times/base/supervisor.rb +14 -21
  17. data/lib/modern_times/base/supervisor_mbean.rb +4 -6
  18. data/lib/modern_times/base/worker.rb +17 -26
  19. data/lib/modern_times/jms.rb +23 -0
  20. data/lib/modern_times/{hornetq/client.rb → jms/connection.rb} +19 -12
  21. data/lib/modern_times/jms/publisher.rb +91 -0
  22. data/lib/modern_times/jms/supervisor.rb +19 -0
  23. data/lib/modern_times/jms/supervisor_mbean.rb +11 -0
  24. data/lib/modern_times/jms/worker.rb +166 -0
  25. data/lib/modern_times/jms_requestor.rb +10 -0
  26. data/lib/modern_times/jms_requestor/request_handle.rb +33 -0
  27. data/lib/modern_times/jms_requestor/requestor.rb +45 -0
  28. data/lib/modern_times/jms_requestor/supervisor.rb +45 -0
  29. data/lib/modern_times/jms_requestor/supervisor_mbean.rb +21 -0
  30. data/lib/modern_times/jms_requestor/worker.rb +78 -0
  31. data/lib/modern_times/manager.rb +14 -9
  32. data/lib/modern_times/manager_mbean.rb +14 -7
  33. data/lib/modern_times/marshal_strategy.rb +47 -0
  34. data/lib/modern_times/marshal_strategy/bson.rb +31 -0
  35. data/lib/modern_times/marshal_strategy/json.rb +30 -0
  36. data/lib/modern_times/marshal_strategy/ruby.rb +20 -0
  37. data/lib/modern_times/marshal_strategy/string.rb +19 -0
  38. data/lib/modern_times/railsable.rb +17 -74
  39. data/test/base_test.rb +248 -0
  40. data/test/jms.yml +8 -0
  41. data/test/jms_requestor_test.rb +263 -0
  42. data/test/jms_test.rb +296 -0
  43. data/test/marshal_strategy_test.rb +39 -0
  44. metadata +49 -46
  45. data/examples/requestor/hornetq.yml +0 -14
  46. data/examples/simple/hornetq.yml +0 -14
  47. data/lib/modern_times/hornetq.rb +0 -11
  48. data/lib/modern_times/hornetq/marshal_strategy.rb +0 -3
  49. data/lib/modern_times/hornetq/marshal_strategy/json.rb +0 -17
  50. data/lib/modern_times/hornetq/marshal_strategy/ruby.rb +0 -17
  51. data/lib/modern_times/hornetq/marshal_strategy/string.rb +0 -17
  52. data/lib/modern_times/hornetq/publisher.rb +0 -65
  53. data/lib/modern_times/hornetq/supervisor.rb +0 -22
  54. data/lib/modern_times/hornetq/supervisor_mbean.rb +0 -12
  55. data/lib/modern_times/hornetq/worker.rb +0 -127
  56. data/lib/modern_times/hornetq_requestor.rb +0 -9
  57. data/lib/modern_times/hornetq_requestor/request_handle.rb +0 -49
  58. data/lib/modern_times/hornetq_requestor/requestor.rb +0 -48
  59. data/lib/modern_times/hornetq_requestor/worker.rb +0 -29
  60. data/lib/modern_times/thread.rb +0 -16
  61. data/test/base/worker_test.rb +0 -38
  62. data/test/messaging/worker_manager_test.rb +0 -58
  63. data/test/messaging/worker_test.rb +0 -58
  64. data/test/worker_manager_test.rb +0 -48
data/test/base_test.rb ADDED
@@ -0,0 +1,248 @@
1
+ require 'modern_times'
2
+ require 'shoulda'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+
6
+ class DummyWorker
7
+ include ModernTimes::Base::Worker
8
+ attr_reader :opts, :setup_called, :count
9
+
10
+ @@mutex = Mutex.new
11
+
12
+ def self.reset
13
+ @@total_count = 0
14
+ @@workers = []
15
+ end
16
+
17
+ self.reset
18
+
19
+ def self.total_count
20
+ @@total_count
21
+ end
22
+
23
+ def self.workers
24
+ @@workers
25
+ end
26
+
27
+ def initialize(opts={})
28
+ @opts = opts
29
+ @count = 0
30
+ @@mutex.synchronize do
31
+ @@workers << self
32
+ end
33
+ end
34
+
35
+ # One time initialization prior to first thread
36
+ def setup
37
+ @setup_called = true
38
+ end
39
+
40
+ def start
41
+ @stopped = false
42
+ while !@stopped do
43
+ sleep 1
44
+ @count += 1
45
+ @@mutex.synchronize do
46
+ @@total_count += 1
47
+ end
48
+ end
49
+ end
50
+
51
+ def stop
52
+ @stopped = true
53
+ end
54
+
55
+ def status
56
+ "index=#{index}"
57
+ end
58
+ end
59
+
60
+ class Dummy2Worker < DummyWorker
61
+
62
+ end
63
+
64
+ class BaseTest < Test::Unit::TestCase
65
+ context 'a worker with no name specified' do
66
+ setup do
67
+ @worker = DummyWorker.new
68
+ @worker.index = 2
69
+ @worker.thread = 'dummy thread'
70
+ @supervisor = DummyWorker.create_supervisor('dummy_manager', {:foo => 1})
71
+ end
72
+
73
+ should "have default name and proper supervisor and attributes" do
74
+ assert_equal('Dummy', @worker.class.default_name)
75
+ assert_equal('Dummy', @supervisor.name)
76
+ assert_equal(2, @worker.index)
77
+ assert_equal('dummy thread', @worker.thread)
78
+ assert_equal(ModernTimes::Base::Supervisor, @supervisor.class)
79
+ assert_equal('dummy_manager', @supervisor.manager)
80
+ end
81
+ end
82
+
83
+ context 'a worker with name specified' do
84
+ setup do
85
+ @worker = DummyWorker.new(:name => 'Foo')
86
+ @supervisor = DummyWorker.create_supervisor('dummy_manager', {:name => 'Foo'})
87
+ end
88
+
89
+ should "have name specified and proper supervisor and attributes" do
90
+ assert_equal('Foo', @supervisor.name)
91
+ end
92
+ end
93
+
94
+ context 'a default worker' do
95
+ setup do
96
+ DummyWorker.reset
97
+ @start_time = Time.now
98
+ @manager = ModernTimes::Manager.new
99
+ @manager.add(DummyWorker, 2, {:foo => 42})
100
+ sleep 5
101
+ @manager.stop
102
+ @manager.join
103
+ @end_time = Time.now
104
+ end
105
+
106
+ should "be performing work" do
107
+ w = DummyWorker.workers
108
+ w = w.reverse if w[0].index == 1
109
+ assert_equal 2, w.size
110
+ (0..1).each do |i|
111
+ worker = w[i]
112
+ assert worker.count >= 3
113
+ assert worker.count <= 8
114
+ assert worker.index == i
115
+ assert worker.status == "index=#{worker.index}"
116
+ end
117
+ assert DummyWorker.total_count >= 8
118
+ assert DummyWorker.total_count <= 14
119
+ assert (@end_time-@start_time) < 14.0
120
+ assert w[0].setup_called
121
+ assert !w[1].setup_called
122
+ end
123
+ end
124
+
125
+ context 'a disallowed worker' do
126
+ setup do
127
+ DummyWorker.reset
128
+ @manager = ModernTimes::Manager.new(:domain => 'DisallowedWorker')
129
+ @manager.allowed_workers = []
130
+ end
131
+
132
+ should "not be allowed" do
133
+ e = assert_raises ModernTimes::Exception do
134
+ @manager.add(DummyWorker, 2, {:foo => 42})
135
+ end
136
+ assert_match %r%is not an allowed worker%, e.message
137
+
138
+ e = assert_raises ModernTimes::Exception do
139
+ @manager.add('FdajfsdklasdfWorker', 2, {:foo => 42})
140
+ end
141
+ assert_match %r%Invalid class%, e.message
142
+ end
143
+ end
144
+
145
+ context 'multiple workers' do
146
+ setup do
147
+ DummyWorker.reset
148
+ @manager = ModernTimes::Manager.new(:domain => 'AllowedWorker')
149
+ @manager.allowed_workers = [DummyWorker, Dummy2Worker]
150
+ @manager.add(DummyWorker, 2, {:foo => 42})
151
+ @manager.add(DummyWorker, 1, {:name => 'OtherDummy'})
152
+ @manager.add(Dummy2Worker, 2, {:name => 'SecondDummy'})
153
+ sleep 5
154
+ @manager.stop
155
+ @manager.join
156
+ @end_time = Time.now
157
+ end
158
+
159
+ should "work" do
160
+ w = DummyWorker.workers
161
+ s = @manager.supervisors
162
+ assert_equal 5, w.size
163
+ assert_equal 3, s.size
164
+ (0..4).each do |i|
165
+ worker = w[i]
166
+ assert worker.count >= 3
167
+ assert worker.count <= 8
168
+ end
169
+ assert DummyWorker.total_count >= 20
170
+ assert DummyWorker.total_count <= 35
171
+ super_names = s.map {|sup| sup.name}.sort
172
+ assert_equal ['Dummy', 'OtherDummy', 'SecondDummy'], super_names
173
+ end
174
+ end
175
+
176
+ context 'manager with persist_file set' do
177
+ setup do
178
+ DummyWorker.reset
179
+ persist_file = "/tmp/modern_times_persist_#{$$}.state"
180
+ @manager = ModernTimes::Manager.new(:domain => 'PersistManager', :persist_file => persist_file)
181
+ @manager.allowed_workers = [DummyWorker, Dummy2Worker]
182
+ @manager.add(DummyWorker, 2, {:foo => 42})
183
+ @manager.add(DummyWorker, 1, {:name => 'OtherDummy'})
184
+ @manager.add(Dummy2Worker, 2, {:name => 'SecondDummy'})
185
+ @manager.stop
186
+ @manager.join
187
+ DummyWorker.reset
188
+ @manager2 = ModernTimes::Manager.new(:domain => 'PersistManager2', :persist_file => persist_file)
189
+ sleep 5
190
+ @manager2.stop
191
+ @manager2.join
192
+ FileUtils.rm persist_file
193
+ end
194
+
195
+ should "recreate workers and supervisors correctly" do
196
+ w = DummyWorker.workers
197
+ s = @manager.supervisors
198
+ assert_equal 5, w.size
199
+ assert_equal 3, s.size
200
+ (0..4).each do |i|
201
+ worker = w[i]
202
+ assert worker.count >= 3
203
+ assert worker.count <= 8
204
+ end
205
+ assert DummyWorker.total_count >= 20
206
+ assert DummyWorker.total_count <= 35
207
+ super_names = s.map {|sup| sup.name}.sort
208
+ assert_equal ['Dummy', 'OtherDummy', 'SecondDummy'], super_names
209
+ end
210
+ end
211
+
212
+ context 'manager' do
213
+ setup do
214
+ DummyWorker.reset
215
+ persist_file = "/tmp/modern_times_persist_#{$$}.state"
216
+ @domain = 'JMXManagerDomain'
217
+ @manager = ModernTimes::Manager.new(:domain => @domain)
218
+ @manager.allowed_workers = [DummyWorker, Dummy2Worker]
219
+
220
+ @server = JMX.simple_server
221
+ @client = JMX.connect
222
+ @manager_mbean = @client[ModernTimes.manager_mbean_object_name(@domain)]
223
+ end
224
+
225
+ teardown do
226
+ @manager.stop
227
+ @manager.join
228
+ @server.stop
229
+ end
230
+
231
+ should "allow JMX to start and query workers" do
232
+ @manager_mbean.start_worker('DummyWorker', 2, '{"foo":42}')
233
+ @manager_mbean.start_worker('DummyWorker', 1, '{"name":"OtherDummy"}')
234
+ @manager_mbean.start_worker('Dummy2Worker', 2, '{"name":"SecondDummy"}')
235
+ #puts "allowed workers=#{@manager_mbean.allowed_workers[0].class.name}"
236
+ assert_equal ['DummyWorker', 'Dummy2Worker'], @manager_mbean.allowed_workers.to_a
237
+
238
+ dummy_bean = @client[ModernTimes.supervisor_mbean_object_name(@domain, 'Dummy')]
239
+ other_dummy_bean = @client[ModernTimes.supervisor_mbean_object_name(@domain, 'OtherDummy')]
240
+ second_dummy_bean = @client[ModernTimes.supervisor_mbean_object_name(@domain, 'SecondDummy')]
241
+ assert 2, dummy_bean.worker_count
242
+ assert 1, other_dummy_bean.worker_count
243
+ assert 2, second_dummy_bean.worker_count
244
+ other_dummy_bean.worker_count = 3
245
+ assert 3, other_dummy_bean.worker_count
246
+ end
247
+ end
248
+ end
data/test/jms.yml ADDED
@@ -0,0 +1,8 @@
1
+ :factory: org.apache.activemq.ActiveMQConnectionFactory
2
+ :broker_url: tcp://localhost:61616
3
+ :require_jars:
4
+ - <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/activemq-all-*.jar")[0] %>
5
+ #- <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/lib/optional/slf4j-log4j*.jar")[0] %>
6
+ #- <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/lib/optional/log4j-*.jar")[0] %>
7
+ #:username: myuser
8
+ #:password: mypassword
@@ -0,0 +1,263 @@
1
+ require 'modern_times'
2
+ require 'shoulda'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+ require 'erb'
6
+
7
+ # NOTE: This test requires a running ActiveMQ server
8
+
9
+ module HashTest
10
+ module ModuleMethods
11
+ def create_obj(i)
12
+ {
13
+ 'foo' => 1,
14
+ 'bar' => {
15
+ 'message' => i,
16
+ 'dummy' => "Message #{i}"
17
+ },
18
+ # Only YAML will maintain symbols
19
+ :zulu => :rugger
20
+ }
21
+ end
22
+
23
+ def parse_obj(obj)
24
+ obj['answer']
25
+ end
26
+ end
27
+
28
+ def self.included(base)
29
+ base.extend(ModuleMethods)
30
+ end
31
+
32
+ def request(obj)
33
+ {
34
+ 'answer' => obj['bar']['message']
35
+ }
36
+ end
37
+ end
38
+
39
+ module BSONTest
40
+ extend ModernTimes::MarshalStrategy::BSON
41
+ include HashTest
42
+ end
43
+
44
+ module JSONTest
45
+ extend ModernTimes::MarshalStrategy::JSON
46
+ include HashTest
47
+ end
48
+
49
+ module RubyTest
50
+ extend ModernTimes::MarshalStrategy::Ruby
51
+
52
+ class MyClass
53
+ attr_reader :i
54
+ def initialize(i)
55
+ @i = i
56
+ end
57
+ end
58
+
59
+ def self.create_obj(i)
60
+ MyClass.new(i)
61
+ end
62
+
63
+ def self.parse_obj(obj)
64
+ obj.i-10
65
+ end
66
+
67
+ def request(obj)
68
+ return MyClass.new(obj.i+10)
69
+ end
70
+ end
71
+
72
+ module StringTest
73
+ extend ModernTimes::MarshalStrategy::String
74
+
75
+ def self.create_obj(i)
76
+ "Message #{i}"
77
+ end
78
+
79
+ def self.parse_obj(obj)
80
+ if obj =~ /^Returning (\d+)$/
81
+ $1.to_i
82
+ else
83
+ raise "Unknown message: #{obj}"
84
+ end
85
+ end
86
+
87
+ def request(str)
88
+ if str =~ /^Message (\d+)$/
89
+ "Returning #{$1}"
90
+ else
91
+ raise "Unknown message: #{str}"
92
+ end
93
+ end
94
+ end
95
+
96
+ class DefaultWorker
97
+ include ModernTimes::JMSRequestor::Worker
98
+ end
99
+
100
+ class SleepWorker
101
+ include ModernTimes::JMSRequestor::Worker
102
+ marshal :string
103
+
104
+ def request(i)
105
+ sleep i.to_i
106
+ return i
107
+ end
108
+ end
109
+
110
+ class JMSRequestorTest < Test::Unit::TestCase
111
+
112
+ @@server = JMX.simple_server
113
+ @@client = JMX.connect
114
+
115
+ context 'jms' do
116
+ setup do
117
+ config = YAML.load(ERB.new(File.read(File.join(File.dirname(__FILE__), 'jms.yml'))).result(binding))
118
+ ModernTimes::JMS::Connection.init(config)
119
+ end
120
+
121
+ teardown do
122
+ end
123
+
124
+ #[BSONTest, JSONTest, RubyTest, StringTest].each do |marshal_module|
125
+ [BSONTest, JSONTest, StringTest].each do |marshal_module|
126
+ marshal_module.name =~ /(.*)Test/
127
+ marshal_type = $1
128
+
129
+ context "marshaling with #{marshal_type}" do
130
+ setup do
131
+ @domain = "Uniquize_#{marshal_module.name}"
132
+ @manager = ModernTimes::Manager.new(:domain => @domain)
133
+ end
134
+
135
+ teardown do
136
+ if @manager
137
+ @manager.stop
138
+ @manager.join
139
+ end
140
+ end
141
+
142
+ should "reply correctly with multiple threads" do
143
+ DefaultWorker.send(:include, marshal_module)
144
+ DefaultWorker.send(:marshal, marshal_module)
145
+ @manager.add(DefaultWorker, 10)
146
+
147
+ sleep 1
148
+
149
+ requestor = ModernTimes::JMSRequestor::Requestor.new(:queue_name => 'Default', :marshal => marshal_module)
150
+ threads = []
151
+ start = Time.now
152
+ (0..9).each do |i|
153
+ threads << Thread.new(i) do |i|
154
+ start = i*10
155
+ range = start..(start+9)
156
+ range.each do |x|
157
+ obj = marshal_module.create_obj(x)
158
+ handle = requestor.request(obj, 2)
159
+ val = marshal_module.parse_obj(handle.read_response)
160
+ assert x == val, "#{i} does not equal #{val}"
161
+ end
162
+ end
163
+ end
164
+ threads.each {|t| t.join}
165
+ end
166
+ end
167
+ end
168
+
169
+ context 'timed requesting' do
170
+ setup do
171
+ @domain = "TimedModernTimes"
172
+ @manager = ModernTimes::Manager.new(:domain => @domain)
173
+ @manager.add(SleepWorker, 10)
174
+ sleep 1
175
+ @requestor = ModernTimes::JMSRequestor::Requestor.new(:queue_name => 'Sleep', :marshal => :string)
176
+ end
177
+
178
+ teardown do
179
+ if @manager
180
+ @manager.stop
181
+ @manager.join
182
+ end
183
+ end
184
+
185
+ should "work correctly if request is complete before the timeout" do
186
+ [[1,0,2,0.8,1.2], [2,1,3,1.6,2.4], [1,2,3,0,8,1.2], [3,1,2,2.8,3.4]].each do |info|
187
+ work_sleep_time, publish_sleep_time, timeout_time, min_time, max_time = info
188
+ threads = []
189
+ start_time = Time.now
190
+ (0..9).each do |i|
191
+ threads << Thread.new(i) do |i|
192
+ handle = @requestor.request(work_sleep_time, timeout_time)
193
+ sleep publish_sleep_time
194
+ if work_sleep_time < timeout_time
195
+ response = handle.read_response.to_i
196
+ assert work_sleep_time == response, "#{work_sleep_time} does not equal #{response}"
197
+ else
198
+ e = assert_raises Timeout::Error do
199
+ response = handle.read_response.to_i
200
+ end
201
+ actual_time = Time.now - start_time
202
+ assert timeout_time-0.1 < actual_time, "Bad timeout #{actual_time}"
203
+ assert timeout_time+0.3 > actual_time, "Bad timeout #{actual_time}"
204
+
205
+ # Give the requests time to complete
206
+ sleep work_sleep_time - timeout_time + 1
207
+ end
208
+ end
209
+ end
210
+ threads.each {|t| t.join}
211
+ total_time = Time.now - start_time
212
+ bean = @@client[ModernTimes.supervisor_mbean_object_name(@domain, 'Sleep')]
213
+ bean_avg = bean.average_response_time
214
+ bean_min = bean.min_response_time
215
+ bean_max = bean.max_response_time
216
+ puts "total=#{total_time} avg=#{bean_avg} min=#{bean_min} max=#{bean_max}"
217
+ all_times = [bean_avg, bean_min, bean_max]
218
+ all_times << total_time if work_sleep_time < timeout_time
219
+ all_times.each do |time_val|
220
+ assert min_time < time_val, "#{time_val} is not between #{min_time} and #{max_time}"
221
+ assert max_time > time_val, "#{time_val} is not between #{min_time} and #{max_time}"
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ # context 'dummy requesting' do
228
+ # setup do
229
+ # workers = [
230
+ # DefaultWorker,
231
+ # Dummy::DefaultWorker,
232
+ # SpecifiedQueueWorker,
233
+ # SpecifiedQueue2Worker,
234
+ # SpecifiedTopicWorker,
235
+ # SpecifiedTopic2Worker,
236
+ # ]
237
+ # workers.each do |worker_klass|
238
+ # worker_klass.send(:include, RubyTest)
239
+ # end
240
+ # ModernTimes::JMS::Publisher.setup_dummy_publishing(workers)
241
+ # end
242
+ #
243
+ # teardown do
244
+ # ModernTimes::JMS::Publisher.clear_dummy_publishing
245
+ # end
246
+ #
247
+ # should "directly call applicable workers" do
248
+ # publish(RubyTest, 100..199, :queue_name => 'Default')
249
+ # publish(RubyTest, 200..299, :queue_name => 'Dummy_Default')
250
+ # publish(RubyTest, 300..499, :queue_name => 'MyQueueName')
251
+ # publish(RubyTest, 500..599, :virtual_topic_name => 'MyTopicName')
252
+ #
253
+ # # DefaultWorker should have 5 instances running with each worker handling between 10-30 messages in the range 100.199
254
+ # assert_worker(nil, DefaultWorker, nil, 1, 100..199, 100, 100, 1)
255
+ # assert_worker(nil, Dummy::DefaultWorker, nil, 1, 200..299, 100, 100, 1)
256
+ # assert_worker(nil, SpecifiedQueueWorker, nil, 1, 300..499, 200, 200, 1)
257
+ # assert_worker(nil, SpecifiedQueue2Worker, nil, 1, 300..499, 200, 200, 1)
258
+ # assert_worker(nil, SpecifiedTopicWorker, nil, 1, 500..599, 100, 100, 1)
259
+ # assert_worker(nil, SpecifiedTopic2Worker, nil, 1, 500..599, 100, 100, 1)
260
+ # end
261
+ # end
262
+ end
263
+ end