modern_times 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +201 -0
- data/README.rdoc +143 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/examples/simple/.gitignore +2 -0
- data/examples/simple/README +24 -0
- data/examples/simple/bar_worker.rb +6 -0
- data/examples/simple/baz_worker.rb +8 -0
- data/examples/simple/hornetq.yml +14 -0
- data/examples/simple/manager.rb +17 -0
- data/examples/simple/publish.rb +35 -0
- data/lib/modern_times/base/supervisor.rb +97 -0
- data/lib/modern_times/base/supervisor_mbean.rb +30 -0
- data/lib/modern_times/base/worker.rb +55 -0
- data/lib/modern_times/base.rb +3 -0
- data/lib/modern_times/exception.rb +4 -0
- data/lib/modern_times/hornetq/client.rb +53 -0
- data/lib/modern_times/hornetq/marshal_strategy/json.rb +17 -0
- data/lib/modern_times/hornetq/marshal_strategy/ruby.rb +17 -0
- data/lib/modern_times/hornetq/marshal_strategy/string.rb +17 -0
- data/lib/modern_times/hornetq/marshal_strategy.rb +3 -0
- data/lib/modern_times/hornetq/publisher.rb +72 -0
- data/lib/modern_times/hornetq/supervisor.rb +19 -0
- data/lib/modern_times/hornetq/supervisor_mbean.rb +12 -0
- data/lib/modern_times/hornetq/worker.rb +121 -0
- data/lib/modern_times/hornetq.rb +11 -0
- data/lib/modern_times/loggable.rb +23 -0
- data/lib/modern_times/manager.rb +92 -0
- data/lib/modern_times/manager_mbean.rb +36 -0
- data/lib/modern_times/railsable.rb +132 -0
- data/lib/modern_times/thread.rb +16 -0
- data/lib/modern_times.rb +13 -0
- data/test/base/worker_test.rb +38 -0
- data/test/messaging/worker_manager_test.rb +58 -0
- data/test/messaging/worker_test.rb +58 -0
- data/test/worker_manager_test.rb +48 -0
- metadata +123 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'hornetq'
|
2
|
+
|
3
|
+
# Handle Messaging and Queuing
|
4
|
+
module ModernTimes
|
5
|
+
module HornetQ
|
6
|
+
module Client
|
7
|
+
# Singleton-ize
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# Initialize the messaging system and connection pool for this VM
|
11
|
+
def init(config)
|
12
|
+
@config = config
|
13
|
+
@connection = ::HornetQ::Client::Connection.new(@config[:connection])
|
14
|
+
# Let's not create a session_pool unless we're going to use it
|
15
|
+
@session_pool_mutex = Mutex.new
|
16
|
+
|
17
|
+
at_exit do
|
18
|
+
close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def invm?
|
23
|
+
@connection.invm?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a session targeted for a consumer (producers should use the session_pool)
|
27
|
+
def create_consumer_session
|
28
|
+
@connection.create_session(config[:session])
|
29
|
+
end
|
30
|
+
|
31
|
+
def session_pool
|
32
|
+
# Don't use the mutex unless we have to!
|
33
|
+
return @session_pool if @session_pool
|
34
|
+
@session_pool_mutex.synchronize do
|
35
|
+
# if it's been created in between the above call and now, return it
|
36
|
+
return @session_pool if @session_pool
|
37
|
+
return @session_pool = @connection.create_session_pool(config[:session])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
ModernTimes.logger.info "Closing #{self.name}"
|
43
|
+
@session_pool.close if @session_pool
|
44
|
+
@connection.close if @connection
|
45
|
+
end
|
46
|
+
|
47
|
+
def config
|
48
|
+
raise "#{self.name} never had it's init method called" unless @config
|
49
|
+
@config
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module HornetQ
|
3
|
+
module MarshalStrategy
|
4
|
+
module JSON
|
5
|
+
def marshal(session, object, durable)
|
6
|
+
message = session.create_message(::HornetQ::Client::Message::TEXT_TYPE, durable)
|
7
|
+
message.body = object.to_json
|
8
|
+
message
|
9
|
+
end
|
10
|
+
|
11
|
+
def unmarshal(msg)
|
12
|
+
JSON::Parser.new(msg.body).parse
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module HornetQ
|
3
|
+
module MarshalStrategy
|
4
|
+
module Ruby
|
5
|
+
def marshal(session, object, durable)
|
6
|
+
message = session.create_message(::HornetQ::Client::Message::BYTES_TYPE, durable)
|
7
|
+
message.body = ::Marshal.dump(object)
|
8
|
+
message
|
9
|
+
end
|
10
|
+
|
11
|
+
def unmarshal(msg)
|
12
|
+
::Marshal.load(msg.body)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module HornetQ
|
3
|
+
module MarshalStrategy
|
4
|
+
module String
|
5
|
+
def marshal(session, object, durable)
|
6
|
+
message = session.create_message(::HornetQ::Client::Message::TEXT_TYPE, durable)
|
7
|
+
message.body = object.to_s
|
8
|
+
message
|
9
|
+
end
|
10
|
+
|
11
|
+
def unmarshal(msg)
|
12
|
+
msg.body
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'hornetq'
|
2
|
+
|
3
|
+
# Protocol independent class to handle Publishing
|
4
|
+
module ModernTimes
|
5
|
+
module HornetQ
|
6
|
+
class Publisher
|
7
|
+
|
8
|
+
# TODO: Possible performance enhancements on producer
|
9
|
+
# setDisableMessageID()
|
10
|
+
# setDisableMessageTimeStamp()
|
11
|
+
# See http://hornetq.sourceforge.net/docs/hornetq-2.1.2.Final/user-manual/en/html/perf-tuning.html
|
12
|
+
# Create producer pool as per above section 46.6?
|
13
|
+
def initialize(address, options={})
|
14
|
+
@address = address
|
15
|
+
@durable = !!options[:durable]
|
16
|
+
if options[:marshal].nil?
|
17
|
+
marshal_module = MarshalStrategy::Ruby
|
18
|
+
elsif options[:marshal].kind_of? Symbol
|
19
|
+
marshal_module = case options[:marshal]
|
20
|
+
when :ruby then MarshalStrategy::Ruby
|
21
|
+
when :string then MarshalStrategy::String
|
22
|
+
when :json then MarshalStrategy::JSON
|
23
|
+
else raise "Invalid marshal strategy: #{options[:marshal]}"
|
24
|
+
end
|
25
|
+
elsif options[:marshal].kind_of? Module
|
26
|
+
marshal_module = options[:marshal]
|
27
|
+
else
|
28
|
+
raise "Invalid marshal strategy: #{options[:marshal]}"
|
29
|
+
end
|
30
|
+
self.extend marshal_module
|
31
|
+
end
|
32
|
+
|
33
|
+
# Publish the given object to the address.
|
34
|
+
def publish(object)
|
35
|
+
Client.session_pool.producer(@address) do |session, producer|
|
36
|
+
message = marshal(session, object, @durable)
|
37
|
+
first_time = true
|
38
|
+
begin
|
39
|
+
producer.send(message)
|
40
|
+
rescue Java::org.hornetq.api.core.HornetQException => e
|
41
|
+
ModernTimes.logger.warn "Received producer exception: #{e.message} with code=#{e.cause.code}"
|
42
|
+
if first_time && e.cause.code == Java::org.hornetq.api.core.HornetQException::UNBLOCKED
|
43
|
+
ModernTimes.logger.info "Retrying the send"
|
44
|
+
first_time = false
|
45
|
+
retry
|
46
|
+
else
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# For non-configured Rails projects, The above publish method will be overridden to
|
54
|
+
# call this publish method instead which calls all the HornetQ workers that
|
55
|
+
# operate on the given address.
|
56
|
+
def dummy_publish(object)
|
57
|
+
@@worker_instances.each do |worker|
|
58
|
+
if worker.kind_of?(Worker) && worker.address_name == @address
|
59
|
+
ModernTimes.logger.debug "Dummy publishing #{object} to #{worker}"
|
60
|
+
worker.perform(object)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.setup_dummy_publishing(workers)
|
66
|
+
@@worker_instances = workers.map {|worker| worker.new}
|
67
|
+
alias_method :real_publish, :publish
|
68
|
+
alias_method :publish, :dummy_publish
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module HornetQ
|
3
|
+
class Supervisor < ModernTimes::Base::Supervisor
|
4
|
+
# Make HornetQ::SupervisorMBean our mbean
|
5
|
+
mbean SupervisorMBean
|
6
|
+
|
7
|
+
attr_reader :message_count
|
8
|
+
|
9
|
+
def initialize(manager, worker_name, opts={})
|
10
|
+
super(manager, worker_name, opts)
|
11
|
+
@message_count = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def incr_message_count
|
15
|
+
@message_count += 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module HornetQ
|
3
|
+
|
4
|
+
# Base Worker Class for any class that will be processing messages from queues
|
5
|
+
class Worker < ModernTimes::Base::Worker
|
6
|
+
# Default to ruby marshaling, but extenders can override as necessary
|
7
|
+
include MarshalStrategy::Ruby
|
8
|
+
|
9
|
+
# Make HornetQ::Supervisor our supervisor
|
10
|
+
supervisor Supervisor
|
11
|
+
|
12
|
+
attr_reader :session, :message_count
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@status = 'initialized'
|
16
|
+
@message_count = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup
|
20
|
+
session = Client.create_consumer_session
|
21
|
+
session.create_queue_ignore_exists(address_name, queue_name, false)
|
22
|
+
session.close
|
23
|
+
end
|
24
|
+
|
25
|
+
def status
|
26
|
+
@status || "Processing message #{message_count}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_message(message)
|
30
|
+
object = unmarshal(message)
|
31
|
+
ModernTimes.logger.debug "#{self}: Received Object: #{object}" if ModernTimes.logger.debug?
|
32
|
+
perform(object)
|
33
|
+
ModernTimes.logger.debug "#{self}: Finished processing message" if ModernTimes.logger.debug?
|
34
|
+
ModernTimes.logger.flush if ModernTimes.logger.respond_to?(:flush)
|
35
|
+
rescue Exception => e
|
36
|
+
ModernTimes.logger.error "#{self}: Messaging Exception: #{e.inspect}\n#{e.backtrace.inspect}"
|
37
|
+
rescue java.lang.Exception => e
|
38
|
+
ModernTimes.logger.error "#{self}: Java Messaging Exception: #{e.inspect}\n#{e.backtrace.inspect}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def perform(object)
|
42
|
+
raise "#{self}: Need to override perform method in #{self.class.name} in order to act on #{object}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.address_name
|
46
|
+
@address_name ||= default_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.queue_name
|
50
|
+
@queue_name ||= default_name
|
51
|
+
end
|
52
|
+
|
53
|
+
def address_name
|
54
|
+
self.class.address_name
|
55
|
+
end
|
56
|
+
|
57
|
+
def queue_name
|
58
|
+
self.class.queue_name
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
"#{address_name}:#{queue_name}:#{index}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Start the event loop for handling messages off the queue
|
66
|
+
def start
|
67
|
+
@session = Client.create_consumer_session
|
68
|
+
@consumer = @session.create_consumer(queue_name)
|
69
|
+
@session.start
|
70
|
+
ModernTimes.logger.debug "#{self}: Starting receive loop"
|
71
|
+
@status = nil
|
72
|
+
while msg = @consumer.receive
|
73
|
+
@message_count += 1
|
74
|
+
supervisor.incr_message_count
|
75
|
+
on_message(msg)
|
76
|
+
msg.acknowledge
|
77
|
+
end
|
78
|
+
@status = 'Exited'
|
79
|
+
ModernTimes.logger.info "Exiting #{self}"
|
80
|
+
rescue Java::org.hornetq.api.core.HornetQException => e
|
81
|
+
if e.cause.code == Java::org.hornetq.api.core.HornetQException::OBJECT_CLOSED
|
82
|
+
# Normal exit
|
83
|
+
@status = 'Exited'
|
84
|
+
ModernTimes.logger.info "#{self}: Exiting due to close"
|
85
|
+
else
|
86
|
+
@status = "Exited with HornetQ exception #{e.message}"
|
87
|
+
ModernTImes.logger.error "#{self} HornetQException: #{e.message}\n#{e.backtrace.join("\n")}"
|
88
|
+
end
|
89
|
+
rescue Exception => e
|
90
|
+
@status = "Exited with exception #{e.message}"
|
91
|
+
ModernTimes.logger.error "#{self}: Exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
92
|
+
rescue java.lang.Exception => e
|
93
|
+
@status = "Exited with java exception #{e.message}"
|
94
|
+
ModernTimes.logger.error "#{self}: Java exception, thread terminating: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def stop
|
98
|
+
@session.close if @session
|
99
|
+
end
|
100
|
+
|
101
|
+
#########
|
102
|
+
protected
|
103
|
+
#########
|
104
|
+
|
105
|
+
# Create a queue, assigned to the specified address
|
106
|
+
# Every time a message arrives, the perform instance method
|
107
|
+
# will be called. The parameter to the method is the Ruby
|
108
|
+
# object supplied when the message was sent
|
109
|
+
def self.address(address_name, opts={})
|
110
|
+
@address_name = address_name.to_s
|
111
|
+
#Messaging::Client.on_message(address, queue_name) do |object|
|
112
|
+
# self.send(method.to_sym, object)
|
113
|
+
#end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.queue(queue_name, opts={})
|
117
|
+
@queue_name = queue_name.to_s
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'modern_times/hornetq/client'
|
2
|
+
require 'modern_times/hornetq/marshal_strategy'
|
3
|
+
require 'modern_times/hornetq/publisher'
|
4
|
+
require 'modern_times/hornetq/supervisor_mbean'
|
5
|
+
require 'modern_times/hornetq/supervisor'
|
6
|
+
require 'modern_times/hornetq/worker'
|
7
|
+
|
8
|
+
module ModernTimes
|
9
|
+
module HornetQ
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module Loggable
|
3
|
+
def logger
|
4
|
+
@logger ||= (rails_logger || default_logger)
|
5
|
+
end
|
6
|
+
|
7
|
+
def rails_logger
|
8
|
+
(defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
|
9
|
+
(defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_logger
|
13
|
+
require 'logger'
|
14
|
+
l = Logger.new($stdout)
|
15
|
+
l.level = Logger::INFO
|
16
|
+
l
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger=(logger)
|
20
|
+
@logger = logger
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
class Manager
|
3
|
+
attr_accessor :allowed_workers
|
4
|
+
|
5
|
+
def initialize(config={})
|
6
|
+
@config = config
|
7
|
+
@domain = config[:domain] || 'ModernTimes'
|
8
|
+
@supervisors = []
|
9
|
+
@jmx_server = JMX::MBeanServer.new
|
10
|
+
bean = ManagerMBean.new("#{@domain}.Manager", "Manager", self)
|
11
|
+
@jmx_server.register_mbean(bean, "#{@domain}:type=Manager")
|
12
|
+
persist_file = config[:persist_file]
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(worker_klass, num_workers, worker_options)
|
16
|
+
ModernTimes.logger.info "Starting #{worker_klass} with #{num_workers} workers with options #{worker_options.inspect}"
|
17
|
+
unless worker_klass.kind_of?(Class)
|
18
|
+
begin
|
19
|
+
worker_klass = Object.const_get(worker_klass.to_s)
|
20
|
+
rescue
|
21
|
+
raise ModernTimes::Exception.new("Invalid class: #{worker_klass}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
if @allowed_workers && !@allowed_workers.include?(worker_klass)
|
25
|
+
raise ModernTimes::Exception.new("Error: #{worker_klass.name} is not an allowed worker")
|
26
|
+
end
|
27
|
+
supervisor = worker_klass.create_supervisor(self)
|
28
|
+
mbean = supervisor.create_mbean(@domain)
|
29
|
+
@supervisors << supervisor
|
30
|
+
supervisor.worker_count = num_workers
|
31
|
+
@jmx_server.register_mbean(mbean, "#{@domain}:worker=#{worker_klass.name},type=Worker")
|
32
|
+
ModernTimes.logger.info "Started #{worker_klass.name} with #{num_workers} workers"
|
33
|
+
rescue Exception => e
|
34
|
+
ModernTimes.logger.error "Exception trying to add #{worker_klass.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
35
|
+
rescue java.lang.Exception => e
|
36
|
+
ModernTimes.logger.error "Java exception trying to add #{worker_klass.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
return if @started
|
41
|
+
@started = true
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop
|
46
|
+
@stopped = true
|
47
|
+
@supervisors.each { |supervisor| supervisor.stop }
|
48
|
+
end
|
49
|
+
|
50
|
+
def join
|
51
|
+
while !@stopped
|
52
|
+
sleep 1
|
53
|
+
end
|
54
|
+
@supervisors.each { |supervisor| supervisor.join }
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop_on_signal
|
58
|
+
['HUP', 'INT', 'TERM'].each do |signal_name|
|
59
|
+
Signal.trap(signal_name) do
|
60
|
+
ModernTimes.logger.info "Caught #{signal_name}"
|
61
|
+
stop
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def persist_file=(file)
|
67
|
+
@persist_file = file
|
68
|
+
return unless file
|
69
|
+
@persist_file = file
|
70
|
+
if File.exist?(file)
|
71
|
+
hash = YAML.load_file(file)
|
72
|
+
hash.each do |worker_klass, count|
|
73
|
+
add(worker_klass, count)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def save_persist_state
|
79
|
+
return unless @persist_file
|
80
|
+
hash = {}
|
81
|
+
@supervisors.each do |supervisor|
|
82
|
+
hash[supervisor.worker_name] = {
|
83
|
+
:worker_count => supervisor.worker_count,
|
84
|
+
:options => supervisor.worker_options
|
85
|
+
}
|
86
|
+
end
|
87
|
+
File.open(@persist_file, 'w') do |out|
|
88
|
+
YAML.dump(hash, out )
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'jmx'
|
2
|
+
|
3
|
+
module ModernTimes
|
4
|
+
class ManagerMBean < RubyDynamicMBean
|
5
|
+
attr_reader :manager
|
6
|
+
rw_attribute :foobar, :int, "Number of workers"
|
7
|
+
|
8
|
+
def initialize(name, description, manager)
|
9
|
+
super(name, description)
|
10
|
+
@manager = manager
|
11
|
+
end
|
12
|
+
|
13
|
+
operation 'Allowed workers'
|
14
|
+
returns :list
|
15
|
+
def allowed_workers
|
16
|
+
all = manager.allowed_workers || ['No Restrictions']
|
17
|
+
all.map {|worker_klass| worker_klass.name }
|
18
|
+
end
|
19
|
+
|
20
|
+
operation 'Start worker'
|
21
|
+
parameter :string, "worker", "The worker class to start"
|
22
|
+
parameter :int, "count", "Number of workers"
|
23
|
+
returns :string
|
24
|
+
def start_worker(worker, count)
|
25
|
+
ModernTimes.logger.debug "Attempting to start #{worker} with count=#{count}"
|
26
|
+
manager.add(worker, count)
|
27
|
+
return 'Successfuly started'
|
28
|
+
rescue Exception => e
|
29
|
+
ModernTimes.logger.error "Exception starting worker #{worker}: {e.message}\n\t#{e.backtrace.join("\n\t")}"
|
30
|
+
return "Exception starting worker #{worker}: {e.message}"
|
31
|
+
rescue java.lang.Exception => e
|
32
|
+
ModernTimes.logger.error "Java exception starting worker #{worker}: {e.message}\n\t#{e.backtrace.join("\n\t")}"
|
33
|
+
return "Java exception starting worker #{worker}: {e.message}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
module Railsable
|
3
|
+
def init_rails
|
4
|
+
if cfg = YAML.load_file(File.join(Rails.root, "config", "hornetq.yml"))[Rails.env]
|
5
|
+
ModernTimes.logger.info "Messaging Enabled"
|
6
|
+
ModernTimes::HornetQ::Client.init(cfg)
|
7
|
+
@is_hornetq_enabled = true
|
8
|
+
|
9
|
+
# Need to start the HornetQ Server in this VM
|
10
|
+
if ModernTimes::HornetQ::Client.invm?
|
11
|
+
@server = ::HornetQ::Server.create_server('hornetq://invm')
|
12
|
+
@server.start
|
13
|
+
|
14
|
+
# Handle messages within this process
|
15
|
+
@manager = ModernTimes::Manager.new
|
16
|
+
if worker_cfg = cfg[:workers]
|
17
|
+
worker_cfg.each do |klass, count|
|
18
|
+
@manager.add(klass, count)
|
19
|
+
end
|
20
|
+
else
|
21
|
+
rails_workers.each do |klass|
|
22
|
+
@manager.add(klass, 1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
at_exit do
|
27
|
+
@manager.stop if @manager
|
28
|
+
@server.stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create Async Queue and handle requests
|
33
|
+
#self.async_queue_name = self.async_address = "Messaging::Client.async"
|
34
|
+
#self.on_message(self.async_address, self.async_queue_name) do |request|
|
35
|
+
# self.async_on_message(request)
|
36
|
+
#end
|
37
|
+
|
38
|
+
else
|
39
|
+
Rails.logger.info "Messaging disabled"
|
40
|
+
@is_hornetq_enabled = false
|
41
|
+
ModernTimes::HornetQ::Publisher.setup_dummy_publishing(rails_workers)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_rails_manager
|
46
|
+
cfg = YAML.load_file(File.join(Rails.root, "config", "hornetq.yml"))[Rails.env]
|
47
|
+
raise "No valid configuration" unless cfg
|
48
|
+
ModernTimes::HornetQ::Client.init(cfg)
|
49
|
+
|
50
|
+
manager = ModernTimes::Manager.new
|
51
|
+
manager.stop_on_signal
|
52
|
+
manager.allowed_workers = rails_workers
|
53
|
+
manager.persist_file = cfg[:persist_file] || File.join(Rails.root, "log", "modern_times.persist")
|
54
|
+
return manager
|
55
|
+
end
|
56
|
+
|
57
|
+
def rails_workers
|
58
|
+
@rails_workers ||= begin
|
59
|
+
workers = []
|
60
|
+
Dir["#{Rails.root}/app/workers/*_worker.rb"].each do |file|
|
61
|
+
require file
|
62
|
+
workers << File.basename(file).sub(/\.rb$/, '').classify.constantize
|
63
|
+
end
|
64
|
+
workers
|
65
|
+
end
|
66
|
+
#file = "#{Rails.root}/config/workers.yml"
|
67
|
+
#raise "No worker config file #{file}" unless File.exist?(file)
|
68
|
+
end
|
69
|
+
|
70
|
+
def hornetq_enabled?
|
71
|
+
@is_hornetq_enabled
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
## Protocol independent class to handle Messaging and Queuing
|
78
|
+
#module Messaging
|
79
|
+
# class Client
|
80
|
+
#
|
81
|
+
# # Publish to the specified address
|
82
|
+
# # If the supplied object is kind_of? String, then a string is published
|
83
|
+
# # Otherwise the Ruby Object is unmarshaled and sent as a Binary message
|
84
|
+
#
|
85
|
+
# # Asynchronously invoke the supplied method
|
86
|
+
# #
|
87
|
+
# # Example:
|
88
|
+
# # Messaging::Client.async(Dashboard, :update_dashboard_for_inquiry, xml_response)
|
89
|
+
# def self.async(klass, method, *param)
|
90
|
+
# @@session_pool.producer(self.async_address) do |session, producer|
|
91
|
+
# request = AsyncRequest.new
|
92
|
+
# request.klass = if klass.kind_of?(String)
|
93
|
+
# klass
|
94
|
+
# elsif klass.kind_of?(Symbol)
|
95
|
+
# klass.to_s
|
96
|
+
# else
|
97
|
+
# klass.name.to_s
|
98
|
+
# end
|
99
|
+
# request.method = method
|
100
|
+
# request.params = *param
|
101
|
+
# message = session.create_message(4,false) #HornetQ::Client::Message::BYTES_TYPE
|
102
|
+
# message['format'] = 'ruby'
|
103
|
+
# message.body = Marshal.dump(request)
|
104
|
+
# producer.send(message)
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# private
|
109
|
+
# # Call the specified class passing in the required parameters
|
110
|
+
# # If the method matches a class method, it is called, otherwise
|
111
|
+
# # an instance of the class is created and the method is called
|
112
|
+
# # on the new instance
|
113
|
+
# #
|
114
|
+
# # Note: Instance methods are more expensive because the class is instantiated
|
115
|
+
# # for every call
|
116
|
+
# def self.async_on_message(request)
|
117
|
+
# klass = request.klass.constantize
|
118
|
+
# method = request.method.to_sym
|
119
|
+
# if klass.respond_to?(method, false)
|
120
|
+
# klass.send(method, *request.params)
|
121
|
+
# else
|
122
|
+
# klass.new.send(method, *request.params)
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# # Passed as the request message, used to hold all required parameters
|
127
|
+
# class AsyncRequest
|
128
|
+
# attr_accessor :klass, :method, :params
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# end
|
132
|
+
#end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ModernTimes
|
2
|
+
class Thread < ::Thread
|
3
|
+
def initialize(&block)
|
4
|
+
super() do
|
5
|
+
begin
|
6
|
+
yield
|
7
|
+
rescue => e
|
8
|
+
ModernTimes.logger.fatal("Thread #{self} died due to exception #{e.message}\n#{e.backtrace.join("\n")}")
|
9
|
+
ensure
|
10
|
+
ActiveRecord::Base.clear_active_connections!() if Module.const_get('ActiveRecord') rescue nil
|
11
|
+
ModernTimes.logger.flush if ModernTimes.logger.respond_to?(:flush)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/modern_times.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'modern_times/exception'
|
3
|
+
require 'modern_times/base'
|
4
|
+
require 'modern_times/hornetq'
|
5
|
+
require 'modern_times/manager_mbean'
|
6
|
+
require 'modern_times/manager'
|
7
|
+
require 'modern_times/loggable'
|
8
|
+
require 'modern_times/railsable'
|
9
|
+
|
10
|
+
module ModernTimes
|
11
|
+
extend ModernTimes::Loggable
|
12
|
+
extend ModernTimes::Railsable
|
13
|
+
end
|