module Refinery #:nodoc:
# A daemon provides a thread to run workers in.
class Daemon
include Refinery::Loggable
include Refinery::Configurable
include Refinery::Utilities
include Refinery::Queueable
RUNNING = 'running'
STOPPED = 'stopped'
# The daemon's thread
attr_reader :thread
# The name of the daemon
attr_reader :name
# The settings for the daemon
attr_reader :settings
# The base queue name
attr_reader :queue_name
# Stop the daemon
def stop
self.state = STOPPED
end
# Return the daemon state
def state
@state ||= RUNNING
end
# Set the daemon state.
def state=(state)
@state = state
end
protected :state
# Return true if the daemon state is running.
def running?
state == RUNNING
end
# Initialize the daemon.
#
# * server: The server instance
# * name: The processor name
# * waiting_queue: The waiting queue that provides messages to be processed
# * error_queue: The queue where errors are posted.
# * done_queue: The queue for messages that have been processed.
# * settings: The settings hash from the config.
#
# The settings hash may contain the following options:
# * visibility: The time in seconds that the message is hidden
# in the queue.
def initialize(server, name, queue_prefix='', settings={})
Refinery::Server.logger.debug "Starting daemon"
@server = server
@name = name
@settings = settings
queue_name = settings['queue'] || name
queue_name = "#{queue_prefix}#{queue_name}"
logger.debug "Using queue #{queue_name}"
@queue_name = queue_name
@thread = Thread.new(self) do |daemon|
logger.debug "Running daemon thread: #{name} (settings: #{settings.inspect})"
while(running?)
with_queue("#{queue_name}_waiting") do |waiting_queue|
while (message = waiting_queue.receive(settings['visibility']))
worker = load_worker_class(name).new(self)
begin
result, run_time = worker.run(decode_message(message.body))
if result
with_queue("#{queue_name}_done") do |done_queue|
done_message = {
'host_info' => host_info,
'original' => message.body,
'run_time' => run_time
}
logger.debug "Sending 'done' message to #{done_queue.name}"
done_queue.send_message(encode_message(done_message))
end
logger.debug "Deleting message from queue"
message.delete()
end
rescue Exception => e
with_queue("#{queue_name}_error") do |error_queue|
error_message = {
'error' => {
'message' => e.message,
'class' => e.class.name
},
'host_info' => host_info,
'original' => message.body
}
logger.error "Sending 'error' message to #{error_queue.name}: #{e.message}"
error_queue.send_message(encode_message(error_message))
end
message.delete()
end
end
sleep(settings['sleep'] || 5)
end
end
logger.debug "Exiting daemon thread"
end
end
# A hash of worker classes
def workers
@workers ||= {}
end
private
# Load the appropriate worker class
def load_worker_class(name)
source_file = "#{@server.workers_directory}/#{name}.rb"
if File.exist?(source_file)
modified_at = File.mtime(source_file)
if workers[name] != modified_at
logger.debug "Loading #{source_file}"
load(source_file)
workers[name] = modified_at
end
else
raise SourceFileNotFound, "Source file not found: #{source_file}"
end
Object.const_get(camelize("#{name}_worker"))
end
end
end