modern_times 0.1.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.
- 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
|