modern_times 0.1.2 → 0.2.0

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