modern_times 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.md +9 -0
- data/README.rdoc +3 -8
- data/TODO.md +10 -0
- data/VERSION +1 -1
- data/examples/simple/.gitignore +1 -1
- data/examples/simple/README +3 -2
- data/examples/simple/bar_worker.rb +0 -1
- data/examples/simple/baz_worker.rb +0 -1
- data/examples/simple/manager.rb +1 -1
- data/lib/modern_times/base/supervisor.rb +1 -6
- data/lib/modern_times/base/worker.rb +4 -0
- data/lib/modern_times/jms/publish_handle.rb +4 -4
- data/lib/modern_times/jms/publisher.rb +27 -19
- data/lib/modern_times/jms/request_worker.rb +54 -23
- data/lib/modern_times/jms/worker.rb +89 -44
- data/lib/modern_times/manager.rb +0 -1
- data/test/jms.yml +2 -2
- data/test/jms_fail_test.rb +149 -0
- data/test/jms_requestor_block_test.rb +8 -5
- data/test/jms_requestor_test.rb +4 -4
- data/test/jms_test.rb +42 -33
- metadata +6 -4
- data/test/jms_failure_test.rb +0 -128
data/History.md
ADDED
data/README.rdoc
CHANGED
@@ -10,9 +10,6 @@ Still alpha but approaching beta. API still subject to change.
|
|
10
10
|
|
11
11
|
== Features/Problems:
|
12
12
|
|
13
|
-
* jms_test and jms_requestor_test don't exit
|
14
|
-
* Railsable needs testing
|
15
|
-
* failure_queue needs testing
|
16
13
|
* Currently tested only for ActiveMQ
|
17
14
|
|
18
15
|
== Install:
|
@@ -21,8 +18,6 @@ Still alpha but approaching beta. API still subject to change.
|
|
21
18
|
|
22
19
|
== Rails Usage:
|
23
20
|
|
24
|
-
TODO: This section needs updating for JMS
|
25
|
-
|
26
21
|
Create config/jms.yml which might look as follows:
|
27
22
|
|
28
23
|
development_server: &defaults
|
@@ -40,13 +35,13 @@ Create config/jms.yml which might look as follows:
|
|
40
35
|
<<: *defaults
|
41
36
|
:broker_url: tcp://stage2:61616
|
42
37
|
:username: myuser
|
43
|
-
:password:
|
38
|
+
:password: mypassword
|
44
39
|
|
45
40
|
production:
|
46
41
|
<<: *defaults
|
47
|
-
:broker_url: failover://(tcp://msg1:61616,tcp://msg2:61616)?randomize=false&
|
42
|
+
:broker_url: failover://(tcp://msg1:61616,tcp://msg2:61616)?randomize=false&initialReconnectDelay=100&useExponentialBackOff=true&maxCacheSize=524288&trackMessages=true
|
48
43
|
:username: myuser
|
49
|
-
:password:
|
44
|
+
:password: mypassword
|
50
45
|
|
51
46
|
In development and test mode, you will notice that there is no configuration defined. In this case, published messages will cause
|
52
47
|
synchronous calls to the Worker's perform method which matches the destination queue or topic.
|
data/TODO.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
TODO
|
2
|
+
========
|
3
|
+
|
4
|
+
* jms/consumer.rb - is it needed? is dummy stuff needed?
|
5
|
+
* Better API documentation
|
6
|
+
* jms_test and jms_requestor_test don't exit
|
7
|
+
* Responses aren't non-persistent? Why are timed out responses ending up in the DLQ?
|
8
|
+
* RequestWorker needs option for failure queue as well as sending exception in response
|
9
|
+
* Batch file handling coming soon
|
10
|
+
* More thorough test coverage, especially the various options, Railsable, etc.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.2
|
data/examples/simple/.gitignore
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
data
|
2
|
-
|
2
|
+
modern_times.yml
|
data/examples/simple/README
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# Step 0
|
2
2
|
# Follow the directions for configuring jms.yml located in examples/README
|
3
|
+
# Perform steps 1-4 in separate terminals
|
3
4
|
|
4
5
|
# Step 1
|
5
6
|
# Start a JMS Server
|
6
7
|
|
7
8
|
# Step 2
|
8
9
|
# Start up the manager
|
9
|
-
rm -f modern_times.
|
10
|
+
rm -f modern_times.yml
|
10
11
|
jruby manager.rb
|
11
12
|
|
12
13
|
# Step 3
|
@@ -18,7 +19,7 @@ jruby manager.rb
|
|
18
19
|
# Enter BazWorker for worker, 3 for count, clear the options field, and click the start_worker button.
|
19
20
|
|
20
21
|
# Step 4
|
21
|
-
# Publish 10 messages to the BarWorker and
|
22
|
+
# Publish 10 messages to the BarWorker and 5 to the BazWorker
|
22
23
|
jruby publish.rb 10 5
|
23
24
|
|
24
25
|
# Step 5
|
data/examples/simple/manager.rb
CHANGED
@@ -11,7 +11,7 @@ require 'baz_worker'
|
|
11
11
|
config = YAML.load(ERB.new(File.read(File.join(File.dirname(__FILE__), '..', 'jms.yml'))).result(binding))
|
12
12
|
ModernTimes::JMS::Connection.init(config)
|
13
13
|
|
14
|
-
manager = ModernTimes::Manager.new(:persist_file => 'modern_times.
|
14
|
+
manager = ModernTimes::Manager.new(:persist_file => 'modern_times.yml')
|
15
15
|
manager.stop_on_signal
|
16
16
|
manager.allowed_workers = [BarWorker,BazWorker]
|
17
17
|
#manager.add(BarWorker, 2, {})
|
@@ -14,7 +14,6 @@ module ModernTimes
|
|
14
14
|
@worker_options = worker_options
|
15
15
|
@workers = []
|
16
16
|
@worker_mutex = Mutex.new
|
17
|
-
@failure_mutex = Mutex.new
|
18
17
|
end
|
19
18
|
|
20
19
|
def worker_count
|
@@ -70,11 +69,7 @@ module ModernTimes
|
|
70
69
|
end
|
71
70
|
|
72
71
|
def join
|
73
|
-
@workers.each { |worker| worker.
|
74
|
-
end
|
75
|
-
|
76
|
-
def failure(worker, message)
|
77
|
-
|
72
|
+
@workers.each { |worker| worker.join }
|
78
73
|
end
|
79
74
|
|
80
75
|
def mbean_name(domain)
|
@@ -63,7 +63,7 @@ module ModernTimes
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def dummy_read_response(timeout, &block)
|
66
|
-
raise "Invalid call to read_response for #{@publisher}, not setup for responding" unless @publisher.response
|
66
|
+
raise "Invalid call to read_response for #{@publisher}, not setup for responding" unless @publisher.response?
|
67
67
|
do_read_response(nil, timeout, &block)
|
68
68
|
end
|
69
69
|
|
@@ -183,11 +183,11 @@ module ModernTimes
|
|
183
183
|
message = consumer.receive(100)
|
184
184
|
end
|
185
185
|
return nil unless message
|
186
|
-
@name = message['worker']
|
187
|
-
if error_yaml = message['exception']
|
186
|
+
@name = message['mt:worker']
|
187
|
+
if error_yaml = message['mt:exception']
|
188
188
|
return ModernTimes::RemoteException.from_hash(YAML.load(error_yaml))
|
189
189
|
end
|
190
|
-
marshaler = ModernTimes::MarshalStrategy.find(message['marshal'] || :ruby)
|
190
|
+
marshaler = ModernTimes::MarshalStrategy.find(message['mt:marshal'] || :ruby)
|
191
191
|
return marshaler.unmarshal(message.data)
|
192
192
|
end
|
193
193
|
|
@@ -4,23 +4,24 @@ require 'jms'
|
|
4
4
|
module ModernTimes
|
5
5
|
module JMS
|
6
6
|
class Publisher
|
7
|
-
attr_reader :producer_options, :persistent, :marshaler, :reply_queue
|
7
|
+
attr_reader :producer_options, :persistent, :marshaler, :reply_queue
|
8
8
|
|
9
9
|
@@dummy_publishing = false
|
10
10
|
|
11
11
|
# Parameters:
|
12
12
|
# One of the following must be specified
|
13
|
-
# :queue_name
|
14
|
-
# :topic_name
|
15
|
-
# :virtual_topic_name
|
13
|
+
# :queue_name => String: Name of the Queue to publish to
|
14
|
+
# :topic_name => String: Name of the Topic to publish to (In general this should not be used as all worker threads will receive all messages)
|
15
|
+
# :virtual_topic_name => String: Name of the Virtual Topic to publish to
|
16
16
|
# (ActiveMQ only, see http://activemq.apache.org/virtual-destinations.html
|
17
|
-
# :destination
|
17
|
+
# :destination => Explicit javax::Jms::Destination to use
|
18
18
|
# Optional:
|
19
|
-
# :
|
20
|
-
# :
|
21
|
-
#
|
22
|
-
# :
|
23
|
-
# :
|
19
|
+
# :time_to_live => expiration time in ms for the message
|
20
|
+
# :persistent => true or false (defaults to false)
|
21
|
+
# :marshal => Symbol: One of :ruby, :string, :json, :bson, :yaml or any registered types (See ModernTimes::MarshalStrategy), defaults to :ruby
|
22
|
+
# :response => if true, a temporary reply queue will be setup for handling responses (defaults to false unless any other mt_response_* options are set)
|
23
|
+
# :response_time_to_live => expiration time in ms for the response message(s)
|
24
|
+
# :response_persistent => true or false for the response message(s), set to false if you don't want timed out messages ending up in the DLQ (defaults to true unless mt_response_time_to_live is set)
|
24
25
|
def initialize(options)
|
25
26
|
raise "ModernTimes::JMS::Connection has not been initialized" unless ModernTimes::JMS::Connection.inited? || @@dummy_publishing
|
26
27
|
producer_keys = [:queue_name, :topic_name, :virtual_topic_name, :destination]
|
@@ -33,22 +34,27 @@ module ModernTimes
|
|
33
34
|
virtual_topic_name = @real_producer_options.delete(:virtual_topic_name)
|
34
35
|
@real_producer_options[:topic_name] = "VirtualTopic.#{virtual_topic_name}" if virtual_topic_name
|
35
36
|
|
36
|
-
|
37
|
-
#@persistent = options[:persistent] ? ::JMS::DeliveryMode::PERSISTENT : ::JMS::DeliveryMode::NON_PERSISTENT
|
38
|
-
@persistent = options[:persistent] ? :persistent : :non_persistent
|
37
|
+
@persistent_sym = options[:persistent] ? :persistent : :non_persistent
|
39
38
|
@marshal = options[:marshal] || :ruby
|
40
39
|
@marshaler = ModernTimes::MarshalStrategy.find(@marshal)
|
41
40
|
@time_to_live = options[:time_to_live]
|
41
|
+
@response_time_to_live_str = options[:response_time_to_live] && options[:response_time_to_live].to_s
|
42
|
+
@response_persistent_str = nil
|
43
|
+
@response_persistent_str = (!!options[:response_persistent]).to_s unless options[:response_persistent].nil?
|
42
44
|
|
45
|
+
@is_response = options[:response] || !@response_time_to_live_str.nil? || !@response_persistent_str.nil?
|
43
46
|
@reply_queue = nil
|
44
|
-
|
45
|
-
if !@@dummy_publishing && @response
|
47
|
+
if !@@dummy_publishing && @is_response
|
46
48
|
ModernTimes::JMS::Connection.session_pool.session do |session|
|
47
49
|
@reply_queue = session.create_destination(:queue_name => :temporary)
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
54
|
+
def response?
|
55
|
+
@is_response
|
56
|
+
end
|
57
|
+
|
52
58
|
# Publish the given object to the address.
|
53
59
|
def publish(object, props={})
|
54
60
|
start = Time.now
|
@@ -56,9 +62,11 @@ module ModernTimes
|
|
56
62
|
Connection.session_pool.producer(@real_producer_options) do |session, producer|
|
57
63
|
producer.time_to_live = @time_to_live if @time_to_live
|
58
64
|
message = ModernTimes::JMS.create_message(session, @marshaler, object)
|
59
|
-
message.jms_delivery_mode_sym
|
60
|
-
message.jms_reply_to
|
61
|
-
message['marshal']
|
65
|
+
message.jms_delivery_mode_sym = @persistent_sym
|
66
|
+
message.jms_reply_to = @reply_queue if @reply_queue
|
67
|
+
message['mt:marshal'] = @marshal.to_s
|
68
|
+
message['mt:response:time_to_live'] = @response_time_to_live_str if @response_time_to_live_str
|
69
|
+
message['mt:response:persistent'] = @response_persistent_str unless @response_persistent_str.nil?
|
62
70
|
props.each do |key, value|
|
63
71
|
message.send("#{key}=", value)
|
64
72
|
end
|
@@ -75,7 +83,7 @@ module ModernTimes
|
|
75
83
|
# Model real queue marshaling/unmarshaling
|
76
84
|
trans_object = @marshaler.unmarshal(@marshaler.marshal(object))
|
77
85
|
@@workers.each do |worker|
|
78
|
-
if ModernTimes::JMS.same_destination?(@producer_options, worker.
|
86
|
+
if ModernTimes::JMS.same_destination?(@producer_options, worker.destination_options)
|
79
87
|
if worker.kind_of?(RequestWorker)
|
80
88
|
ModernTimes.logger.debug "Dummy request publishing #{trans_object} to #{worker}"
|
81
89
|
m = worker.marshaler
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ModernTimes
|
2
2
|
module JMS
|
3
3
|
|
4
|
-
# Base Worker Class for any class that will be processing
|
4
|
+
# Base Worker Class for any class that will be processing requests from queues and replying
|
5
5
|
module RequestWorker
|
6
6
|
include Worker
|
7
7
|
# Dummy requesting needs access to this
|
@@ -17,6 +17,11 @@ module ModernTimes
|
|
17
17
|
# Get the response marshaler, defaulting to the request marshaler
|
18
18
|
@response_options
|
19
19
|
end
|
20
|
+
|
21
|
+
# By default, exceptions don't get forwarded to a fail queue (they get returned to the caller)
|
22
|
+
def default_fail_queue_target
|
23
|
+
false
|
24
|
+
end
|
20
25
|
end
|
21
26
|
|
22
27
|
def self.included(base)
|
@@ -32,40 +37,66 @@ module ModernTimes
|
|
32
37
|
@marshal_type = (response_options[:marshal] || :ruby).to_s
|
33
38
|
@marshaler = MarshalStrategy.find(@marshal_type)
|
34
39
|
# Time in msec until the message gets discarded, should be more than the timeout on the requestor side
|
35
|
-
@time_to_live = response_options[:time_to_live]
|
40
|
+
@time_to_live = response_options[:time_to_live]
|
41
|
+
@persistent = response_options[:persistent]
|
36
42
|
end
|
37
43
|
|
38
44
|
def perform(object)
|
39
45
|
response = request(object)
|
40
|
-
|
41
|
-
producer.time_to_live = @time_to_live
|
42
|
-
reply_message = ModernTimes::JMS.create_message(session, @marshaler, response)
|
43
|
-
reply_message.jms_correlation_id = message.jms_message_id
|
44
|
-
reply_message.jms_delivery_mode = ::JMS::DeliveryMode::NON_PERSISTENT
|
45
|
-
reply_message['worker'] = self.name
|
46
|
-
reply_message['marshal'] = @marshal_type
|
47
|
-
producer.send(reply_message)
|
48
|
-
end
|
46
|
+
send_response(@marshal_type, @marshaler, response)
|
49
47
|
rescue Exception => e
|
50
|
-
|
48
|
+
on_exception(e)
|
49
|
+
end
|
50
|
+
|
51
|
+
def request(object)
|
52
|
+
raise "#{self}: Need to override request method in #{self.class.name} in order to act on #{object}"
|
53
|
+
end
|
54
|
+
|
55
|
+
#########
|
56
|
+
protected
|
57
|
+
#########
|
58
|
+
|
59
|
+
def on_exception(e)
|
60
|
+
begin
|
61
|
+
stat = send_response(:string, ModernTimes::MarshalStrategy::String, "Exception: #{e.message}") do |reply_message|
|
62
|
+
reply_message['mt:exception'] = ModernTimes::RemoteException.new(e).to_hash.to_yaml
|
63
|
+
end
|
64
|
+
rescue Exception => e
|
65
|
+
ModernTimes.logger.error("Exception in exception reply: #{e.message}")
|
66
|
+
log_backtrace(e)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Send it on to the fail queue if it was explicitly set
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sending marshaler is redundant but saves a lookup
|
74
|
+
def send_response(marshal_type, marshaler, object)
|
75
|
+
return false unless message.reply_to
|
51
76
|
begin
|
52
77
|
session.producer(:destination => message.reply_to) do |producer|
|
53
|
-
|
54
|
-
|
78
|
+
# For time_to_live and jms_deliver_mode, first use the local response_options if they're' set, otherwise
|
79
|
+
# use the value from the message attributes if they're' set
|
80
|
+
time_to_live = @time_to_live || message['mt:response:time_to_live']
|
81
|
+
persistent = @persistent
|
82
|
+
persistent = (message['mt:response:persistent'] == 'true') if persistent.nil? && message['mt:response:persistent']
|
83
|
+
# If persistent isn't set anywhere, then default to true unless time_to_live has been set
|
84
|
+
persistent = !time_to_live if persistent.nil?
|
85
|
+
producer.time_to_live = time_to_live.to_i if time_to_live
|
86
|
+
reply_message = ModernTimes::JMS.create_message(session, marshaler, object)
|
55
87
|
reply_message.jms_correlation_id = message.jms_message_id
|
56
|
-
|
57
|
-
reply_message
|
58
|
-
reply_message['
|
88
|
+
# The reply is persistent if we explicitly set it or if we don't expire
|
89
|
+
reply_message.jms_delivery_mode_sym = persistent ? :persistent : :non_persistent
|
90
|
+
reply_message['mt:marshal'] = marshal_type.to_s
|
91
|
+
reply_message['mt:worker'] = self.name
|
92
|
+
yield reply_message if block_given?
|
59
93
|
producer.send(reply_message)
|
60
94
|
end
|
61
95
|
rescue Exception => e
|
62
|
-
ModernTimes.logger.error
|
96
|
+
ModernTimes.logger.error {"Error attempting to send response: #{e.message}"}
|
97
|
+
log_backtrace(e)
|
63
98
|
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
def request(object)
|
68
|
-
raise "#{self}: Need to override request method in #{self.class.name} in order to act on #{object}"
|
99
|
+
return true
|
69
100
|
end
|
70
101
|
end
|
71
102
|
end
|
@@ -5,7 +5,7 @@ module ModernTimes
|
|
5
5
|
|
6
6
|
# Base Worker Class for any class that will be processing messages from topics or queues
|
7
7
|
# By default, it will consume messages from a queue with the class name minus the Worker postfix.
|
8
|
-
# For example, the queue call is
|
8
|
+
# For example, the queue call is unnecessary as it will default to a value of 'Foo' anyways:
|
9
9
|
# class FooWorker < ModernTimes::JMS::Worker
|
10
10
|
# queue 'Foo'
|
11
11
|
# def perform(obj)
|
@@ -34,7 +34,7 @@ module ModernTimes
|
|
34
34
|
module Worker
|
35
35
|
include ModernTimes::Base::Worker
|
36
36
|
|
37
|
-
attr_reader :session, :error_count, :time_track
|
37
|
+
attr_reader :session, :error_count, :time_track, :destination_options
|
38
38
|
attr_accessor :message
|
39
39
|
|
40
40
|
module ClassMethods
|
@@ -42,15 +42,6 @@ module ModernTimes
|
|
42
42
|
Supervisor.new(manager, self, {}, worker_options)
|
43
43
|
end
|
44
44
|
|
45
|
-
def destination_options
|
46
|
-
options = dest_options.dup
|
47
|
-
# Default the queue name to the Worker name if a destinations hasn't been specified
|
48
|
-
if options.keys.select {|k| [:virtual_topic_name, :queue_name, :destination].include?(k)}.empty?
|
49
|
-
options[:queue_name] = default_name
|
50
|
-
end
|
51
|
-
return options
|
52
|
-
end
|
53
|
-
|
54
45
|
def virtual_topic(name, opts={})
|
55
46
|
# ActiveMQ only
|
56
47
|
dest_options[:virtual_topic_name] = name.to_s
|
@@ -60,18 +51,30 @@ module ModernTimes
|
|
60
51
|
dest_options[:queue_name] = name.to_s
|
61
52
|
end
|
62
53
|
|
63
|
-
|
54
|
+
def dest_options
|
64
55
|
@dest_options ||= {}
|
65
56
|
end
|
66
57
|
|
67
|
-
|
68
|
-
|
58
|
+
# Set the fail_queue
|
59
|
+
# target =>
|
60
|
+
# boolean
|
61
|
+
# true - exceptions in the worker will cause the message to be forwarded to the queue of <default-name>Fail
|
62
|
+
# For instance, an Exception in FooWorker#perform will forward the message to the queue FooFail
|
63
|
+
# false - exceptions will not result in the message being forwarded to a fail queue
|
64
|
+
# string - equivalent to true but the string defines the name of the fail queue
|
65
|
+
def fail_queue(target, opts={})
|
66
|
+
@fail_queue_target = name
|
69
67
|
end
|
70
68
|
|
71
|
-
def
|
69
|
+
def fail_queue_target
|
72
70
|
# Don't overwrite if the user set to false, only if it was never set
|
73
|
-
@
|
74
|
-
|
71
|
+
@fail_queue_target
|
72
|
+
end
|
73
|
+
|
74
|
+
# Defines the default value of the fail_queue_target. For extenders of this class, the default will be true
|
75
|
+
# but extenders can change this (RequestWorker returns exceptions to the caller so it defaults to false).
|
76
|
+
def default_fail_queue_target
|
77
|
+
true
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
@@ -82,10 +85,34 @@ module ModernTimes
|
|
82
85
|
|
83
86
|
def initialize(opts={})
|
84
87
|
super
|
88
|
+
@stopped = false
|
85
89
|
@status = 'initialized'
|
86
|
-
# Supervisor will ask for counts in a separate thread
|
87
90
|
@time_track = ModernTimes::TimeTrack.new
|
88
91
|
@error_count = 0
|
92
|
+
@message_mutex = Mutex.new
|
93
|
+
|
94
|
+
@destination_options = self.class.dest_options.dup
|
95
|
+
# Default the queue name to the Worker name if a destinations hasn't been specified
|
96
|
+
if @destination_options.keys.select {|k| [:virtual_topic_name, :queue_name, :destination].include?(k)}.empty?
|
97
|
+
@destination_options[:queue_name] = self.name
|
98
|
+
end
|
99
|
+
|
100
|
+
@real_destination_options = @destination_options.dup
|
101
|
+
virtual_topic_name = @real_destination_options.delete(:virtual_topic_name)
|
102
|
+
@real_destination_options[:queue_name] = "Consumer.#{name}.VirtualTopic.#{virtual_topic_name}" if virtual_topic_name
|
103
|
+
|
104
|
+
target = opts[:fail_queue]
|
105
|
+
target = self.class.fail_queue_target if target.nil?
|
106
|
+
target = self.class.default_fail_queue_target if target.nil?
|
107
|
+
if target == true
|
108
|
+
@fail_queue_name = "#{name}Fail"
|
109
|
+
elsif target == false
|
110
|
+
@fail_queue_name = nil
|
111
|
+
elsif target.kind_of?(String)
|
112
|
+
@fail_queue_name = target
|
113
|
+
else
|
114
|
+
raise "Invalid fail queue: #{target}"
|
115
|
+
end
|
89
116
|
end
|
90
117
|
|
91
118
|
def message_count
|
@@ -96,25 +123,20 @@ module ModernTimes
|
|
96
123
|
@status || "Processing message #{@time_track.total_count}"
|
97
124
|
end
|
98
125
|
|
99
|
-
def real_destination_options
|
100
|
-
options = self.class.destination_options
|
101
|
-
virtual_topic_name = options.delete(:virtual_topic_name)
|
102
|
-
options[:queue_name] = "Consumer.#{name}.VirtualTopic.#{virtual_topic_name}" if virtual_topic_name
|
103
|
-
return options
|
104
|
-
end
|
105
|
-
|
106
126
|
# Start the event loop for handling messages off the queue
|
107
127
|
def start
|
108
128
|
@session = Connection.create_consumer_session
|
109
|
-
@consumer = @session.consumer(real_destination_options)
|
129
|
+
@consumer = @session.consumer(@real_destination_options)
|
110
130
|
@session.start
|
111
131
|
|
112
132
|
ModernTimes.logger.debug "#{self}: Starting receive loop"
|
113
133
|
@status = nil
|
114
|
-
while msg = @consumer.receive
|
134
|
+
while !@stopped && msg = @consumer.receive
|
115
135
|
@time_track.perform do
|
116
|
-
|
117
|
-
|
136
|
+
@message_mutex.synchronize do
|
137
|
+
on_message(msg)
|
138
|
+
msg.acknowledge
|
139
|
+
end
|
118
140
|
end
|
119
141
|
ModernTimes.logger.info {"#{self}::on_message (#{('%.1f' % (@time_track.last_time*1000.0))}ms)"} if ModernTimes::JMS::Connection.log_times?
|
120
142
|
end
|
@@ -132,46 +154,69 @@ module ModernTimes
|
|
132
154
|
rescue Exception => e
|
133
155
|
@status = "Exited with exception #{e.message}"
|
134
156
|
ModernTimes.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
135
|
-
rescue java.lang.Exception => e
|
136
|
-
@status = "Exited with java exception #{e.message}"
|
137
|
-
ModernTimes.logger.error "#{self}: Java exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
138
157
|
end
|
139
158
|
|
140
159
|
def stop
|
141
|
-
@
|
142
|
-
|
160
|
+
@stopped = true
|
161
|
+
# Don't clobber the session before a reply
|
162
|
+
@message_mutex.synchronize do
|
163
|
+
@consumer.close if @consumer
|
164
|
+
@session.close if @session
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def perform(object)
|
169
|
+
raise "#{self}: Need to override perform method in #{self.class.name} in order to act on #{object}"
|
170
|
+
end
|
171
|
+
|
172
|
+
def to_s
|
173
|
+
"#{@real_destination_options.to_a.join('=>')}:#{index}"
|
174
|
+
end
|
175
|
+
|
176
|
+
#########
|
177
|
+
protected
|
178
|
+
#########
|
179
|
+
|
180
|
+
def fail_queue_name
|
181
|
+
@fail_queue_name
|
143
182
|
end
|
144
183
|
|
145
184
|
def on_message(message)
|
146
185
|
@message = message
|
147
|
-
marshaler = ModernTimes::MarshalStrategy.find(message['marshal'] || :ruby)
|
186
|
+
marshaler = ModernTimes::MarshalStrategy.find(message['mt:marshal'] || :ruby)
|
148
187
|
object = marshaler.unmarshal(message.data)
|
149
188
|
ModernTimes.logger.debug {"#{self}: Received Object: #{object}"}
|
150
189
|
perform(object)
|
151
190
|
rescue Exception => e
|
152
|
-
@error_count += 1
|
153
191
|
on_exception(e)
|
154
192
|
ensure
|
155
193
|
ModernTimes.logger.debug {"#{self}: Finished processing message"}
|
156
194
|
ModernTimes.logger.flush if ModernTimes.logger.respond_to?(:flush)
|
157
195
|
end
|
158
196
|
|
159
|
-
def
|
160
|
-
|
197
|
+
def increment_error_count
|
198
|
+
@error_count += 1
|
161
199
|
end
|
162
200
|
|
163
201
|
def on_exception(e)
|
164
|
-
|
165
|
-
|
166
|
-
|
202
|
+
increment_error_count
|
203
|
+
ModernTimes.logger.error "#{self}: Messaging Exception: #{e.message}"
|
204
|
+
log_backtrace(e)
|
205
|
+
if fail_queue_name
|
206
|
+
session.producer(:queue_name => fail_queue_name) do |producer|
|
207
|
+
# TODO: Can't add attribute to read-only message?
|
208
|
+
#message['mt:exception'] = ModernTimes::RemoteException.new(e).to_hash.to_yaml
|
167
209
|
producer.send(message)
|
168
210
|
end
|
169
211
|
end
|
212
|
+
rescue Exception => e
|
213
|
+
ModernTimes.logger.error "#{self}: Exception in exception reply: #{e.message}"
|
214
|
+
log_backtrace(e)
|
170
215
|
end
|
171
216
|
|
172
|
-
def
|
173
|
-
"#{
|
217
|
+
def log_backtrace(e)
|
218
|
+
ModernTimes.logger.error "\t#{e.backtrace.join("\n\t")}"
|
174
219
|
end
|
175
220
|
end
|
176
221
|
end
|
177
|
-
end
|
222
|
+
end
|
data/lib/modern_times/manager.rb
CHANGED
data/test/jms.yml
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
:log_times: false
|
4
4
|
:require_jars:
|
5
5
|
- <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/activemq-all-*.jar")[0] %>
|
6
|
-
|
7
|
-
|
6
|
+
- <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/lib/optional/slf4j-log4j*.jar")[0] %>
|
7
|
+
- <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/lib/optional/log4j-*.jar")[0] %>
|
8
8
|
#:username: myuser
|
9
9
|
#:password: mypassword
|
@@ -0,0 +1,149 @@
|
|
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
|
+
class ExceptionWorker
|
10
|
+
include ModernTimes::JMS::Worker
|
11
|
+
def perform(obj)
|
12
|
+
puts "#{name} received #{obj} but raising exception"
|
13
|
+
raise 'foobar'
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_backtrace(e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ExceptionRequestWorker
|
21
|
+
include ModernTimes::JMS::RequestWorker
|
22
|
+
|
23
|
+
def request(obj)
|
24
|
+
puts "#{name} received #{obj} but raising exception"
|
25
|
+
raise 'foobar'
|
26
|
+
end
|
27
|
+
|
28
|
+
def log_backtrace(e)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# This will read from the queue that ExceptionWorker fails to
|
33
|
+
class ExceptionFailWorker
|
34
|
+
include ModernTimes::JMS::Worker
|
35
|
+
|
36
|
+
@@my_hash = {}
|
37
|
+
|
38
|
+
def self.my_obj(name)
|
39
|
+
@@my_hash[name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def perform(obj)
|
43
|
+
puts "#{name} received #{obj}"
|
44
|
+
@@my_hash[name] = obj
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class JMSFailTest < Test::Unit::TestCase
|
49
|
+
|
50
|
+
def assert_fail_queue(queue_name, fail_queue_name, value, is_fail_queue_expected)
|
51
|
+
# Publish to Exception that will throw exception which will put on ExceptionFail queue
|
52
|
+
publisher = ModernTimes::JMS::Publisher.new(:queue_name => queue_name, :marshal => :string)
|
53
|
+
puts "Publishing #{value} to #{queue_name}"
|
54
|
+
publisher.publish(value)
|
55
|
+
sleep 1
|
56
|
+
expected_value = (is_fail_queue_expected ? value : nil)
|
57
|
+
assert_equal expected_value, ExceptionFailWorker.my_obj(fail_queue_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'jms' do
|
61
|
+
setup do
|
62
|
+
config = YAML.load(ERB.new(File.read(File.join(File.dirname(__FILE__), 'jms.yml'))).result(binding))
|
63
|
+
ModernTimes::JMS::Connection.init(config)
|
64
|
+
end
|
65
|
+
|
66
|
+
teardown do
|
67
|
+
end
|
68
|
+
|
69
|
+
context "worker with exception" do
|
70
|
+
setup do
|
71
|
+
@manager = ModernTimes::Manager.new
|
72
|
+
|
73
|
+
# Should receive message on the fail worker when using with default names
|
74
|
+
@manager.add(ExceptionWorker, 1)
|
75
|
+
@manager.add(ExceptionFailWorker, 1)
|
76
|
+
|
77
|
+
# Should receive message on the fail worker when using specified names
|
78
|
+
name = 'ExceptionNameSpecified'
|
79
|
+
@manager.add(ExceptionWorker, 1, :name => name)
|
80
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
81
|
+
|
82
|
+
# Should receive message on the fail worker when using specified names and fail_queue set true
|
83
|
+
name = 'ExceptionFailQueueTrue'
|
84
|
+
@manager.add(ExceptionWorker, 1, :name => name, :fail_queue => true)
|
85
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
86
|
+
|
87
|
+
# Should NOT receive message on the fail worker when using specified names and fail_queue set false
|
88
|
+
name = 'ExceptionFailQueueFalse'
|
89
|
+
@manager.add(ExceptionWorker, 1, :name => name, :fail_queue => false)
|
90
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
91
|
+
|
92
|
+
# Should NOT receive message on the fail worker when using specified names and fail_queue set false
|
93
|
+
name = 'ExceptionFailQueueSpecified'
|
94
|
+
fail_queue = 'MyFailQueue'
|
95
|
+
@manager.add(ExceptionWorker, 1, :name => name, :fail_queue => fail_queue)
|
96
|
+
@manager.add(ExceptionFailWorker, 1, :name => fail_queue)
|
97
|
+
|
98
|
+
# Should NOT receive message on the fail worker
|
99
|
+
name = 'ExceptionRequest'
|
100
|
+
@manager.add(ExceptionRequestWorker, 1)
|
101
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
102
|
+
|
103
|
+
# Should NOT receive message on the fail worker when using specified names
|
104
|
+
name = 'ExceptionRequestNameSpecified'
|
105
|
+
@manager.add(ExceptionRequestWorker, 1, :name => name)
|
106
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
107
|
+
|
108
|
+
# Should receive message on the fail worker when using specified names and fail_queue set true
|
109
|
+
name = 'ExceptionRequestFailQueueTrue'
|
110
|
+
@manager.add(ExceptionRequestWorker, 1, :name => name, :fail_queue => true)
|
111
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
112
|
+
|
113
|
+
# Should NOT receive message on the fail worker when using specified names and fail_queue set false
|
114
|
+
name = 'ExceptionRequestFailQueueFalse'
|
115
|
+
@manager.add(ExceptionRequestWorker, 1, :name => name, :fail_queue => false)
|
116
|
+
@manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
|
117
|
+
|
118
|
+
# Should NOT receive message on the fail worker when using specified names and fail_queue set false
|
119
|
+
name = 'ExceptionRequestFailQueueSpecified'
|
120
|
+
fail_queue = 'MyRequestFailQueue'
|
121
|
+
@manager.add(ExceptionRequestWorker, 1, :name => name, :fail_queue => fail_queue)
|
122
|
+
@manager.add(ExceptionFailWorker, 1, :name => fail_queue)
|
123
|
+
|
124
|
+
sleep 1
|
125
|
+
end
|
126
|
+
|
127
|
+
teardown do
|
128
|
+
if @manager
|
129
|
+
@manager.stop
|
130
|
+
@manager.join
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
should "write fail messages to a fail queue" do
|
135
|
+
assert_fail_queue('Exception', 'ExceptionFail', 'value0', true)
|
136
|
+
assert_fail_queue('ExceptionNameSpecified', 'ExceptionNameSpecifiedFail', 'value1', true)
|
137
|
+
assert_fail_queue('ExceptionFailQueueTrue', 'ExceptionFailQueueTrueFail', 'value2', true)
|
138
|
+
assert_fail_queue('ExceptionFailQueueFalse', 'ExceptionFailQueueFalseFail', 'value3', false)
|
139
|
+
assert_fail_queue('ExceptionFailQueueSpecified', 'MyFailQueue', 'value4', true)
|
140
|
+
|
141
|
+
assert_fail_queue('ExceptionRequest', 'ExceptionRequestFail', 'value5', false)
|
142
|
+
assert_fail_queue('ExceptionRequestNameSpecified', 'ExceptionRequestNameSpecifiedFail', 'value6', false)
|
143
|
+
assert_fail_queue('ExceptionRequestFailQueueTrue', 'ExceptionRequestFailQueueTrueFail', 'value7', true)
|
144
|
+
assert_fail_queue('ExceptionRequestFailQueueFalse', 'ExceptionRequestFailQueueFalseFail', 'value8', false)
|
145
|
+
assert_fail_queue('ExceptionRequestFailQueueSpecified', 'MyRequestFailQueue', 'value9', true)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -29,11 +29,14 @@ class BaseRequestWorker
|
|
29
29
|
raise Exception,'my exception' if self.class.do_exception
|
30
30
|
sleep self.class.sleep_time if self.class.sleep_time
|
31
31
|
end
|
32
|
+
|
33
|
+
def log_backtrace(e)
|
34
|
+
end
|
32
35
|
end
|
33
36
|
|
34
37
|
class CharCountWorker < BaseRequestWorker
|
35
38
|
virtual_topic 'test_string'
|
36
|
-
response :marshal => :bson
|
39
|
+
response :marshal => :bson
|
37
40
|
|
38
41
|
def request(obj)
|
39
42
|
super
|
@@ -45,7 +48,7 @@ end
|
|
45
48
|
|
46
49
|
class LengthWorker < BaseRequestWorker
|
47
50
|
virtual_topic 'test_string'
|
48
|
-
response :marshal => :ruby
|
51
|
+
response :marshal => :ruby
|
49
52
|
|
50
53
|
def request(obj)
|
51
54
|
super
|
@@ -55,7 +58,7 @@ end
|
|
55
58
|
|
56
59
|
class ReverseWorker < BaseRequestWorker
|
57
60
|
virtual_topic 'test_string'
|
58
|
-
response :marshal => :string
|
61
|
+
response :marshal => :string
|
59
62
|
|
60
63
|
def request(obj)
|
61
64
|
super
|
@@ -65,7 +68,7 @@ end
|
|
65
68
|
|
66
69
|
class TripleWorker < BaseRequestWorker
|
67
70
|
virtual_topic 'test_string'
|
68
|
-
response :marshal => :string
|
71
|
+
response :marshal => :string
|
69
72
|
|
70
73
|
def request(obj)
|
71
74
|
super
|
@@ -175,7 +178,7 @@ class JMSRequestorBlockTest < Test::Unit::TestCase
|
|
175
178
|
|
176
179
|
should "handle replies" do
|
177
180
|
|
178
|
-
publisher = ModernTimes::JMS::Publisher.new(:virtual_topic_name => 'test_string', :
|
181
|
+
publisher = ModernTimes::JMS::Publisher.new(:virtual_topic_name => 'test_string', :response_time_to_live => 10000, :marshal => :string)
|
179
182
|
cc_val = {'f' => 1, 'o' => 4, 'b' => 1}
|
180
183
|
|
181
184
|
hash = make_call(publisher, 'fooboo', 2)
|
data/test/jms_requestor_test.rb
CHANGED
@@ -76,7 +76,7 @@ end
|
|
76
76
|
|
77
77
|
class DefaultWorker
|
78
78
|
include ModernTimes::JMS::RequestWorker
|
79
|
-
response :marshal => :yaml
|
79
|
+
response :marshal => :yaml
|
80
80
|
|
81
81
|
def request(obj)
|
82
82
|
options[:tester].request(obj)
|
@@ -85,7 +85,7 @@ end
|
|
85
85
|
|
86
86
|
class SleepWorker
|
87
87
|
include ModernTimes::JMS::RequestWorker
|
88
|
-
response :marshal => :string
|
88
|
+
response :marshal => :string
|
89
89
|
|
90
90
|
def request(i)
|
91
91
|
sleep i.to_i
|
@@ -134,7 +134,7 @@ class JMSRequestorTest < Test::Unit::TestCase
|
|
134
134
|
|
135
135
|
sleep 1
|
136
136
|
|
137
|
-
publisher = ModernTimes::JMS::Publisher.new(:queue_name => 'Default', :marshal => marshal, :
|
137
|
+
publisher = ModernTimes::JMS::Publisher.new(:queue_name => 'Default', :marshal => marshal, :response_time_to_live => 10000)
|
138
138
|
threads = []
|
139
139
|
start = Time.now
|
140
140
|
(0..9).each do |i|
|
@@ -161,7 +161,7 @@ class JMSRequestorTest < Test::Unit::TestCase
|
|
161
161
|
@manager = ModernTimes::Manager.new(:domain => @domain)
|
162
162
|
@manager.add(SleepWorker, 10)
|
163
163
|
sleep 1
|
164
|
-
@publisher = ModernTimes::JMS::Publisher.new(:queue_name => 'Sleep', :marshal => :string, :
|
164
|
+
@publisher = ModernTimes::JMS::Publisher.new(:queue_name => 'Sleep', :marshal => :string, :response_time_to_live => 10000)
|
165
165
|
end
|
166
166
|
|
167
167
|
teardown do
|
data/test/jms_test.rb
CHANGED
@@ -13,8 +13,8 @@ module WorkerHelper
|
|
13
13
|
super
|
14
14
|
@tester = opts[:tester]
|
15
15
|
@@mutex.synchronize do
|
16
|
-
@@workers[self.
|
17
|
-
@@workers[self.
|
16
|
+
@@workers[self.name] ||= []
|
17
|
+
@@workers[self.name] << self
|
18
18
|
end
|
19
19
|
@hash = Hash.new(0)
|
20
20
|
end
|
@@ -23,8 +23,10 @@ module WorkerHelper
|
|
23
23
|
add_message(@tester.translate(obj))
|
24
24
|
end
|
25
25
|
|
26
|
-
def self.workers(
|
27
|
-
|
26
|
+
def self.workers(names)
|
27
|
+
workers = []
|
28
|
+
names.each {|name| workers += @@workers[name]}
|
29
|
+
workers
|
28
30
|
end
|
29
31
|
|
30
32
|
def self.reset_workers
|
@@ -35,7 +37,7 @@ module WorkerHelper
|
|
35
37
|
@hash[i] += 1
|
36
38
|
end
|
37
39
|
|
38
|
-
def
|
40
|
+
def call_count
|
39
41
|
puts "hash=#{@hash.inspect}"
|
40
42
|
@hash.values.reduce(:+)
|
41
43
|
end
|
@@ -144,17 +146,15 @@ class JMSTest < Test::Unit::TestCase
|
|
144
146
|
end
|
145
147
|
end
|
146
148
|
|
147
|
-
def assert_worker(domain,
|
148
|
-
puts "Checking #{
|
149
|
-
|
150
|
-
|
151
|
-
workers = []
|
152
|
-
worker_klasses.each {|klass| workers.concat(WorkerHelper.workers(klass))}
|
149
|
+
def assert_worker(domain, names, worker_count, range, min, max, instance_count)
|
150
|
+
puts "Checking #{names.inspect}"
|
151
|
+
names = [names] unless names.kind_of?(Array)
|
152
|
+
workers = WorkerHelper.workers(names)
|
153
153
|
|
154
154
|
assert_equal worker_count, workers.size
|
155
155
|
all_messages = []
|
156
156
|
workers.each do |worker|
|
157
|
-
msg_count = worker.
|
157
|
+
msg_count = worker.call_count
|
158
158
|
assert msg_count
|
159
159
|
assert msg_count >= min, "#{msg_count} is not between #{min} and #{max}"
|
160
160
|
assert msg_count <= max, "#{msg_count} is not between #{min} and #{max}"
|
@@ -167,8 +167,8 @@ class JMSTest < Test::Unit::TestCase
|
|
167
167
|
|
168
168
|
if domain
|
169
169
|
total_count = 0
|
170
|
-
|
171
|
-
bean = @@client[ModernTimes.supervisor_mbean_object_name(domain,
|
170
|
+
names.each do |name|
|
171
|
+
bean = @@client[ModernTimes.supervisor_mbean_object_name(domain, name)]
|
172
172
|
bean.message_counts.each do |msg_count|
|
173
173
|
total_count += msg_count
|
174
174
|
assert msg_count >= min, "#{msg_count} is not between #{min} and #{max}"
|
@@ -224,19 +224,21 @@ class JMSTest < Test::Unit::TestCase
|
|
224
224
|
sleep 1
|
225
225
|
|
226
226
|
publish(marshal, tester, 100..199, :queue_name => 'Default')
|
227
|
-
publish(marshal, tester, 200..299, :queue_name => '
|
228
|
-
publish(marshal, tester, 300..
|
229
|
-
publish(marshal, tester,
|
227
|
+
publish(marshal, tester, 200..299, :queue_name => 'DefaultClone')
|
228
|
+
publish(marshal, tester, 300..399, :queue_name => 'Dummy_Default')
|
229
|
+
publish(marshal, tester, 400..599, :queue_name => 'MyQueueName')
|
230
|
+
publish(marshal, tester, 600..699, :virtual_topic_name => 'MyTopicName')
|
230
231
|
|
231
232
|
# Let the workers do their thing
|
232
233
|
sleep 5
|
233
234
|
|
234
235
|
# DefaultWorker should have 5 instances running with each worker handling between 10-30 messages in the range 100.199
|
235
|
-
assert_worker(@domain,
|
236
|
-
assert_worker(@domain,
|
237
|
-
assert_worker(@domain,
|
238
|
-
assert_worker(@domain,
|
239
|
-
assert_worker(@domain,
|
236
|
+
assert_worker(@domain, 'Default', 3, 100..199, 30, 36, 1)
|
237
|
+
assert_worker(@domain, 'DefaultClone', 2, 200..299, 45, 55, 1)
|
238
|
+
assert_worker(@domain, 'Dummy_Default', 4, 300..399, 20, 30, 1)
|
239
|
+
assert_worker(@domain, ['SpecifiedQueue', 'SpecifiedQueueClone', 'SpecifiedQueue2'], 7, 400..599, 20, 40, 1)
|
240
|
+
assert_worker(@domain, ['SpecifiedTopic', 'SpecifiedTopicClone'], 5, 600..699, 30, 60, 2)
|
241
|
+
assert_worker(@domain, 'SpecifiedTopic2', 2, 600..699, 35, 65, 1)
|
240
242
|
end
|
241
243
|
end
|
242
244
|
end
|
@@ -246,10 +248,13 @@ class JMSTest < Test::Unit::TestCase
|
|
246
248
|
WorkerHelper.reset_workers
|
247
249
|
workers = [
|
248
250
|
DefaultWorker.new(:tester => RubyTest),
|
251
|
+
DefaultWorker.new(:tester => RubyTest, :name => 'DefaultClone'),
|
249
252
|
Dummy::DefaultWorker.new(:tester => RubyTest),
|
250
253
|
SpecifiedQueueWorker.new(:tester => RubyTest),
|
254
|
+
SpecifiedQueueWorker.new(:tester => RubyTest, :name => 'SpecifiedQueueClone'),
|
251
255
|
SpecifiedQueue2Worker.new(:tester => RubyTest),
|
252
256
|
SpecifiedTopicWorker.new(:tester => RubyTest),
|
257
|
+
SpecifiedTopicWorker.new(:tester => RubyTest, :name => 'SpecifiedTopicClone'),
|
253
258
|
SpecifiedTopic2Worker.new(:tester => RubyTest),
|
254
259
|
]
|
255
260
|
ModernTimes::JMS::Publisher.setup_dummy_publishing(workers)
|
@@ -261,17 +266,21 @@ class JMSTest < Test::Unit::TestCase
|
|
261
266
|
|
262
267
|
should "directly call applicable workers" do
|
263
268
|
publish(:ruby, RubyTest, 100..199, :queue_name => 'Default')
|
264
|
-
publish(:ruby, RubyTest, 200..299, :queue_name => '
|
265
|
-
publish(:ruby, RubyTest, 300..
|
266
|
-
publish(:ruby, RubyTest,
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
assert_worker(nil,
|
271
|
-
assert_worker(nil,
|
272
|
-
assert_worker(nil,
|
273
|
-
assert_worker(nil,
|
274
|
-
assert_worker(nil,
|
269
|
+
publish(:ruby, RubyTest, 200..299, :queue_name => 'DefaultClone')
|
270
|
+
publish(:ruby, RubyTest, 300..399, :queue_name => 'Dummy_Default')
|
271
|
+
publish(:ruby, RubyTest, 400..599, :queue_name => 'MyQueueName')
|
272
|
+
publish(:ruby, RubyTest, 600..699, :virtual_topic_name => 'MyTopicName')
|
273
|
+
|
274
|
+
# The single instance of each class will be called so everyone will have all messages.
|
275
|
+
assert_worker(nil, 'Default', 1, 100..199, 100, 100, 1)
|
276
|
+
assert_worker(nil, 'DefaultClone', 1, 200..299, 100, 100, 1)
|
277
|
+
assert_worker(nil, 'Dummy_Default', 1, 300..399, 100, 100, 1)
|
278
|
+
assert_worker(nil, 'SpecifiedQueue', 1, 400..599, 200, 200, 1)
|
279
|
+
assert_worker(nil, 'SpecifiedQueueClone', 1, 400..599, 200, 200, 1)
|
280
|
+
assert_worker(nil, 'SpecifiedQueue2', 1, 400..599, 200, 200, 1)
|
281
|
+
assert_worker(nil, 'SpecifiedTopic', 1, 600..699, 100, 100, 1)
|
282
|
+
assert_worker(nil, 'SpecifiedTopicClone', 1, 600..699, 100, 100, 1)
|
283
|
+
assert_worker(nil, 'SpecifiedTopic2', 1, 600..699, 100, 100, 1)
|
275
284
|
end
|
276
285
|
end
|
277
286
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: modern_times
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.3.
|
5
|
+
version: 0.3.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Brad Pardee
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2011-05-
|
14
|
+
date: 2011-05-31 00:00:00 -04:00
|
15
15
|
default_executable:
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
@@ -48,9 +48,11 @@ extra_rdoc_files:
|
|
48
48
|
- LICENSE.txt
|
49
49
|
- README.rdoc
|
50
50
|
files:
|
51
|
+
- History.md
|
51
52
|
- LICENSE.txt
|
52
53
|
- README.rdoc
|
53
54
|
- Rakefile
|
55
|
+
- TODO.md
|
54
56
|
- VERSION
|
55
57
|
- examples/README
|
56
58
|
- examples/advanced_requestor/README
|
@@ -105,7 +107,7 @@ files:
|
|
105
107
|
- lib/modern_times/time_track.rb
|
106
108
|
- test/base_test.rb
|
107
109
|
- test/jms.yml
|
108
|
-
- test/
|
110
|
+
- test/jms_fail_test.rb
|
109
111
|
- test/jms_requestor_block_test.rb
|
110
112
|
- test/jms_requestor_test.rb
|
111
113
|
- test/jms_test.rb
|
@@ -157,7 +159,7 @@ test_files:
|
|
157
159
|
- examples/simple/manager.rb
|
158
160
|
- examples/simple/publish.rb
|
159
161
|
- test/base_test.rb
|
160
|
-
- test/
|
162
|
+
- test/jms_fail_test.rb
|
161
163
|
- test/jms_requestor_block_test.rb
|
162
164
|
- test/jms_requestor_test.rb
|
163
165
|
- test/jms_test.rb
|
data/test/jms_failure_test.rb
DELETED
@@ -1,128 +0,0 @@
|
|
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
|
-
class ExceptionWorker
|
10
|
-
include ModernTimes::JMS::Worker
|
11
|
-
|
12
|
-
def perform(obj)
|
13
|
-
puts "ExceptinoWorker received #{obj} but raising exception"
|
14
|
-
raise 'foobar'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# This will read from the queue that ExceptionWorker fails to
|
19
|
-
class ExceptionFailureWorker
|
20
|
-
include ModernTimes::JMS::Worker
|
21
|
-
|
22
|
-
def self.my_obj
|
23
|
-
@@my_obj
|
24
|
-
end
|
25
|
-
|
26
|
-
def perform(obj)
|
27
|
-
puts "ExceptinoFailureWorker received #{obj}"
|
28
|
-
@@my_obj = obj
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class JMSFailureTest < Test::Unit::TestCase
|
33
|
-
|
34
|
-
context 'jms' do
|
35
|
-
setup do
|
36
|
-
config = YAML.load(ERB.new(File.read(File.join(File.dirname(__FILE__), 'jms.yml'))).result(binding))
|
37
|
-
ModernTimes::JMS::Connection.init(config)
|
38
|
-
end
|
39
|
-
|
40
|
-
teardown do
|
41
|
-
end
|
42
|
-
|
43
|
-
context "worker with exception" do
|
44
|
-
setup do
|
45
|
-
@manager = ModernTimes::Manager.new
|
46
|
-
|
47
|
-
@manager.add(ExceptionWorker, 1)
|
48
|
-
@manager.add(ExceptionFailureWorker, 1)
|
49
|
-
|
50
|
-
sleep 1
|
51
|
-
end
|
52
|
-
|
53
|
-
teardown do
|
54
|
-
if @manager
|
55
|
-
@manager.stop
|
56
|
-
@manager.join
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
should "write failure messages to a queue of <name>Failure" do
|
61
|
-
|
62
|
-
# Publish to Exception that will throw exception which will put on ExceptionFailure queue
|
63
|
-
publisher = ModernTimes::JMS::Publisher.new(:queue_name => 'Exception', :marshal => :string)
|
64
|
-
publisher.publish('zulu')
|
65
|
-
sleep 1
|
66
|
-
assert_equal 'zulu', ExceptionFailureWorker.my_obj
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# context "dummy publishing" do
|
71
|
-
# setup do
|
72
|
-
# workers = [
|
73
|
-
# CharCountWorker.new,
|
74
|
-
# CharCountWorker.new(:name => 'CharCount2'),
|
75
|
-
# LengthWorker.new,
|
76
|
-
# LengthWorker.new(:name => 'Length2'),
|
77
|
-
# ReverseWorker.new,
|
78
|
-
# TripleWorker.new,
|
79
|
-
# HolderWorker.new,
|
80
|
-
# ]
|
81
|
-
# ModernTimes::JMS::Publisher.setup_dummy_publishing(workers)
|
82
|
-
# end
|
83
|
-
#
|
84
|
-
# teardown do
|
85
|
-
# ModernTimes::JMS::Publisher.clear_dummy_publishing
|
86
|
-
# end
|
87
|
-
#
|
88
|
-
# should "handle replies" do
|
89
|
-
#
|
90
|
-
# publisher = ModernTimes::JMS::Publisher.new(:virtual_topic_name => 'test_string', :response => true, :marshal => :string)
|
91
|
-
# cc_val = {'f' => 1, 'o' => 4, 'b' => 1}
|
92
|
-
#
|
93
|
-
# hash = make_call(publisher, 'fooboo', 2)
|
94
|
-
# assert_response(hash['CharCount'], :message, cc_val)
|
95
|
-
# assert_response(hash['CharCount2'], :message, cc_val)
|
96
|
-
# assert_response(hash['Length'], :message, 6)
|
97
|
-
# assert_response(hash['Length2'], :message, 6)
|
98
|
-
# assert_response(hash['Reverse'], :message, 'ooboof')
|
99
|
-
# assert_response(hash['Triple'], :message, 'fooboofooboofooboo')
|
100
|
-
#
|
101
|
-
# # Timeouts don't occur when dummy publishing
|
102
|
-
# CharCountWorker.sleep_time = 3
|
103
|
-
# ReverseWorker.sleep_time = 3
|
104
|
-
# hash = make_call(publisher, 'fooboo', 2)
|
105
|
-
# assert_response(hash['CharCount'], :message, cc_val)
|
106
|
-
# assert_response(hash['CharCount2'], :message, cc_val)
|
107
|
-
# assert_response(hash['Length'], :message, 6)
|
108
|
-
# assert_response(hash['Length2'], :message, 6)
|
109
|
-
# assert_response(hash['Reverse'], :message, 'ooboof')
|
110
|
-
# assert_response(hash['Triple'], :message, 'fooboofooboofooboo')
|
111
|
-
# CharCountWorker.sleep_time = nil
|
112
|
-
# ReverseWorker.sleep_time = nil
|
113
|
-
#
|
114
|
-
# CharCountWorker.do_exception = true
|
115
|
-
# TripleWorker.do_exception = true
|
116
|
-
# hash = make_call(publisher, 'fooboo', 2)
|
117
|
-
# assert_exception(hash['CharCount'], :explicit_exception)
|
118
|
-
# assert_exception(hash['CharCount2'], :default_exception)
|
119
|
-
# assert_response(hash['Length'], :message, 6)
|
120
|
-
# assert_response(hash['Length2'], :message, 6)
|
121
|
-
# assert_response(hash['Reverse'], :message, 'ooboof')
|
122
|
-
# assert_exception(hash['Triple'], :default_exception)
|
123
|
-
# CharCountWorker.do_exception = false
|
124
|
-
# TripleWorker.do_exception = false
|
125
|
-
# end
|
126
|
-
# end
|
127
|
-
end
|
128
|
-
end
|