lib/adhearsion/initializer.rb in eric-adhearsion-0.7.999 vs lib/adhearsion/initializer.rb in eric-adhearsion-0.8.0
- old
+ new
@@ -1,8 +1,47 @@
module Adhearsion
+ class << self
+
+ ##
+ # Shuts down the framework.
+ #
+ def self.shutdown!
+ ahn_log "Shutting down gracefully at #{Time.now}."
+ Events.stop!
+ exit
+ end
+
+ end
class PathString < String
+
+ class << self
+
+ ##
+ # Will return a PathString for the application root folder to which the specified arbitrarily nested subfolder belongs.
+ # It works by traversing parent directories looking for the .ahnrc file. If no .ahnrc is found, nil is returned.
+ #
+ # @param [String] folder The path to the directory which should be a
+ # @return [nil] if the subdirectory does not belong to a parent Adhearsion app directory
+ # @return [PathString] if a directory is found
+ #
+ def from_application_subdirectory(folder)
+ folder = File.expand_path folder
+ ahn_rc = nil
+
+ until ahn_rc || folder == "/"
+ possible_ahn_rc = File.join(folder, ".ahnrc")
+ if File.exists?(possible_ahn_rc)
+ ahn_rc = possible_ahn_rc
+ else
+ folder = File.expand_path(folder + "/..")
+ end
+ end
+ ahn_rc ? new(folder) : nil
+ end
+ end
+
attr_accessor :component_path, :dialplan_path, :log_path
def initialize(path)
super
defaults
@@ -25,27 +64,21 @@
block.call
ensure
self.base_path = original_path
end
- def dial_plan_named(name)
- File.join(dialplan_path, name)
- end
-
private
def build_path_for(path)
File.join(to_s, path)
end
end
class Initializer
class << self
def get_rules_from(location)
- if File.directory? location
- location = File.join location, ".ahnrc"
- end
+ location = File.join location, ".ahnrc" if File.directory? location
File.exists?(location) ? YAML.load_file(location) : nil
end
def ahn_root=(path)
if Object.constants.include?("AHN_ROOT")
@@ -53,22 +86,23 @@
else
Object.const_set(:AHN_ROOT, PathString.new(File.expand_path(path)))
end
end
+ def start(*args, &block)
+ new(*args, &block).start
+ end
+
def start_from_init_file(file, ahn_app_path)
return if defined?(@@started) && @@started
- new(ahn_app_path, :loaded_init_files => file)
+ start ahn_app_path, :loaded_init_files => file
end
end
attr_reader :path, :daemon, :pid_file, :log_file, :ahn_app_log_directory
- DEFAULT_RULES = { :pattern => "*.rb",
- :directory => "helpers"}
-
# Creation of pid_files
#
# - You may want to have Adhearsion create a process identification
# file when it boots so that a crash monitoring program such as
# Monit can reboot if necessary or so the init script can kill it
@@ -78,62 +112,155 @@
# one is not created UNLESS it is running in daemon mode, in which
# case one is created. You can force Adhearsion to not create one
# even in daemon mode by supplying "false".
def initialize(path=nil, options={})
@@started = true
- @path = path
- @daemon = options[:daemon]
- @pid_file = options[:pid_file].nil? ? ENV['PID_FILE'] : options[:pid_file]
- @loaded_init_files = options[:loaded_init_files]
+ @path = path
+ @daemon = options[:daemon]
+ @pid_file = options[:pid_file].nil? ? ENV['PID_FILE'] : options[:pid_file]
+ @loaded_init_files = options[:loaded_init_files]
+ end
+
+ def start
self.class.ahn_root = path
+
resolve_pid_file_path
resolve_log_file_path
switch_to_root_directory
catch_termination_signal
bootstrap_rc
- load_all_init_files
- init_modules
daemonize! if should_daemonize?
initialize_log_file
+ load_all_init_files
+ init_components_subsystem
+ init_modules
+ init_events_subsystem
create_pid_file if pid_file
load_components
+ init_events_file
ahn_log "Adhearsion initialized!"
trigger_after_initialized_hooks
- join_framework_threads
+ join_important_threads
+
+ self
end
- def initialize_log_file
- Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
- file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
-
- if should_daemonize?
- Adhearsion::Logging::AdhearsionLogger.outputters = file_logger
- else
- Adhearsion::Logging::AdhearsionLogger.outputters << file_logger
+ def default_pid_path
+ File.join AHN_ROOT, 'adhearsion.pid'
+ end
+
+ def resolve_pid_file_path
+ @pid_file = if pid_file.equal?(true) then default_pid_path
+ elsif pid_file then pid_file
+ elsif pid_file.equal?(false) then nil
+ # FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
+ else @pid_file = @daemon ? default_pid_path : nil
end
- Adhearsion::Logging::DefaultAdhearsionLogger.redefine_outputters
end
def resolve_log_file_path
@ahn_app_log_directory = AHN_ROOT + '/log'
@log_file = File.expand_path(ahn_app_log_directory + "/adhearsion.log")
end
- def create_pid_file(file = pid_file)
- if file
- File.open pid_file, 'w' do |file|
- file.puts Process.pid
+ def switch_to_root_directory
+ Dir.chdir AHN_ROOT
+ end
+
+ def catch_termination_signal
+ %w'INT TERM'.each do |process_signal|
+ trap process_signal do
+ ahn_log "Shutting down gracefully at #{Time.now}."
+ Events.trigger :shutdown
+ exit
end
-
- Hooks::TearDown.create_hook do
- File.delete(pid_file) if File.exists?(pid_file)
+ end
+ end
+
+ ##
+ # This step in the initialization process loads the .ahnrc in the given app folder. With the information in .ahnrc, we
+ # can continue the initialization knowing where certain files are specifically.
+ #
+ def bootstrap_rc
+ rules = self.class.get_rules_from AHN_ROOT
+
+ AHN_CONFIG.ahnrc = rules
+
+ # DEPRECATION: Check if the old paths format is being used. If so, abort and notify.
+ if rules.has_key?("paths") && rules["paths"].kind_of?(Hash)
+ paths = rules["paths"].each_pair do |key,value|
+ if value.kind_of?(Hash)
+ if value.has_key?("directory") || value.has_key?("pattern")
+ puts
+ puts *caller
+ puts
+
+ abort <<-WARNING
+Deprecation Warning
+-------------------
+The (hidden) .ahnrc file in this app is of an older format and needs to be fixed.
+
+There is a rake task to automatically fix it or you can do it manually. Note: it's
+best if you do it manually so you can retain the YAML comments in your .ahnrc file.
+
+The rake task is called "deprecations:fix_ahnrc_path_format".
+
+To do it manually, find all entries in the "paths" section of your ".ahnrc" file
+which look like the following:
+
+paths:
+ key_name_could_be_anything:
+ directory: some_folder
+ pattern: *.rb
+
+Note: the "models" section had this syntax before:
+
+models:
+ directory: models
+ pattern: "*.rb"
+
+The NEW syntax is as follows (using models as an example):
+
+models: models/*.rb
+
+This new format is much cleaner.
+
+Adhearsion will abort until you fix this. Sorry for the incovenience.
+ WARNING
+ end
+ end
end
end
+
+ gems = rules['gems']
+ if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
+ gems.each_pair do |gem_name,properties_hash|
+ if properties_hash && properties_hash["version"]
+ gem gem_name, properties_hash["version"]
+ else
+ gem gem_name
+ end
+ if properties_hash
+ case properties_hash["require"]
+ when Array
+ properties_hash["require"].each { |lib| require lib }
+ when String
+ require properties_hash["require"]
+ end
+ end
+ end
+ end
end
+ def load_all_init_files
+ init_files_from_rc = AHN_CONFIG.files_from_setting("paths", "init").map { |file| File.expand_path(file) }
+ already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
+ (init_files_from_rc - already_loaded_init_files).each { |init| load init }
+ end
+
def init_modules
require 'adhearsion/initializer/database.rb'
require 'adhearsion/initializer/asterisk.rb'
require 'adhearsion/initializer/drb.rb'
require 'adhearsion/initializer/rails.rb'
@@ -144,32 +271,27 @@
DrbInitializer.start if AHN_CONFIG.drb_enabled?
RailsInitializer.start if AHN_CONFIG.rails_enabled?
# FreeswitchInitializer.start if AHN_CONFIG.freeswitch_enabled?
end
- def resolve_pid_file_path
- @pid_file = if pid_file.equal?(true) then default_pid_path
- elsif pid_file then pid_file
- elsif pid_file.equal?(false) then nil
- # FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
- else @pid_file = @daemon ? default_pid_path : nil
+ def init_events_subsystem
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
+ if application_events_files.any?
+ Events.register_callback(:shutdown) do
+ ahn_log.events "Performing a graceful stop of events subsystem"
+ Events.framework_theatre.graceful_stop!
+ end
+ Events.framework_theatre.start!
+ else
+ ahn_log.events.warn 'No entries in the "events" section of .ahnrc. Skipping its initialization.'
end
end
- def switch_to_root_directory
- Dir.chdir AHN_ROOT
- end
-
- def catch_termination_signal
- Hooks::TearDown.catch_termination_signals
- end
-
- def load_all_init_files
- if Paths.manager_for? "init"
- init_files_from_rc = all_inits.map { |file| File.expand_path(file) }
- already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
- (init_files_from_rc - already_loaded_init_files).each { |init| load init }
+ def init_events_file
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
+ application_events_files.each do |file|
+ Events.framework_theatre.load_events_file file
end
end
def should_daemonize?
@daemon || ENV['DAEMON']
@@ -179,38 +301,72 @@
ahn_log "Daemonizing now! Creating #{pid_file}."
extend Adhearsion::CustomDaemonizer
daemonize log_file
end
- def load_components
- ComponentManager.load
- ComponentManager.start
+ def initialize_log_file
+ Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
+ file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
+
+ if should_daemonize?
+ Logging::AdhearsionLogger.outputters = file_logger
+ else
+ Logging::AdhearsionLogger.outputters << file_logger
+ end
+ Logging::DefaultAdhearsionLogger.redefine_outputters
end
- def trigger_after_initialized_hooks
- Hooks::AfterInitialized.trigger_hooks
+ def create_pid_file(file = pid_file)
+ if file
+ File.open pid_file, 'w' do |file|
+ file.puts Process.pid
+ end
+
+ Events.register_callback :shutdown do
+ File.delete(pid_file) if File.exists?(pid_file)
+ end
+ end
end
- def join_framework_threads
- Hooks::ThreadsJoinedAfterInitialized.trigger_hooks
+ def init_components_subsystem
+ @components_directory = File.expand_path "components"
+ if File.directory? @components_directory
+ Components.component_manager = Components::ComponentManager.new @components_directory
+ Kernel.send(:const_set, :COMPONENTS, Components.component_manager.lazy_config_loader)
+ Components.component_manager.globalize_global_scope!
+ Components.component_manager.extend_object_with(Theatre::CallbackDefinitionLoader, :events)
+ else
+ ahn_log.warn "No components directory found. Not initializing any components."
+ end
end
- def bootstrap_rc
- rules = Initializer.get_rules_from(AHN_ROOT) || DEFAULT_RULES
- paths = rules['paths'] || DEFAULT_RULES
- paths.each_pair do |k,v|
- if v.kind_of? Hash
- directory, pattern = v['directory'] || '.', v['pattern'] || '*'
- Paths.manager_for k, :pattern => File.join(directory, pattern)
- else
- directory, pattern = '.', v
- Paths.manager_for k, :pattern => File.join(directory,pattern)
- end
+ def load_components
+ if Components.component_manager
+ Components.component_manager.load_components
end
end
- def default_pid_path
- File.join AHN_ROOT, 'adhearsion.pid'
+ def trigger_after_initialized_hooks
+ Events.trigger_immediately :after_initialized
+ end
+
+ ##
+ # This method will block Thread.main() until calling join() has returned for all Threads in IMPORTANT_THREADS.
+ # Note: IMPORTANT_THREADS won't always contain Thread instances. It simply requires the objects respond to join().
+ #
+ def join_important_threads
+ # Note: we're using this ugly accumulator to ensure that all threads have ended since IMPORTANT_THREADS will almost
+ # certainly change sizes after this method is called.
+ index = 0
+ until index == IMPORTANT_THREADS.size
+ begin
+ IMPORTANT_THREADS[index].join
+ rescue => e
+ ahn_log.error "Error after join()ing Thread #{thread.inspect}. #{e.message}"
+ ensure
+ index = index + 1
+ end
+ end
end
class InitializationFailedError < Exception; end
end
end