rene-adhearsion 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/CHANGELOG +73 -0
  2. data/EVENTS +11 -0
  3. data/LICENSE +456 -0
  4. data/Rakefile +130 -0
  5. data/adhearsion.gemspec +173 -0
  6. data/app_generators/ahn/USAGE +5 -0
  7. data/app_generators/ahn/ahn_generator.rb +96 -0
  8. data/app_generators/ahn/templates/.ahnrc +34 -0
  9. data/app_generators/ahn/templates/README +8 -0
  10. data/app_generators/ahn/templates/Rakefile +25 -0
  11. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  12. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  13. data/app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown +11 -0
  14. data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +48 -0
  15. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +91 -0
  16. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml +34 -0
  17. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +263 -0
  18. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +104 -0
  19. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml +2 -0
  20. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  21. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  22. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml +12 -0
  23. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/README.markdown +3 -0
  24. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +11 -0
  25. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  26. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  27. data/app_generators/ahn/templates/config/startup.rb +83 -0
  28. data/app_generators/ahn/templates/dialplan.rb +3 -0
  29. data/app_generators/ahn/templates/events.rb +32 -0
  30. data/bin/ahn +28 -0
  31. data/bin/ahnctl +68 -0
  32. data/bin/jahn +42 -0
  33. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  34. data/lib/adhearsion.rb +45 -0
  35. data/lib/adhearsion/cli.rb +228 -0
  36. data/lib/adhearsion/component_manager.rb +272 -0
  37. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  38. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  39. data/lib/adhearsion/events_support.rb +84 -0
  40. data/lib/adhearsion/foundation/all.rb +15 -0
  41. data/lib/adhearsion/foundation/blank_slate.rb +3 -0
  42. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  43. data/lib/adhearsion/foundation/event_socket.rb +204 -0
  44. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  45. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  46. data/lib/adhearsion/foundation/numeric.rb +13 -0
  47. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  48. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  49. data/lib/adhearsion/foundation/string.rb +26 -0
  50. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  51. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  52. data/lib/adhearsion/host_definitions.rb +67 -0
  53. data/lib/adhearsion/initializer.rb +395 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +87 -0
  55. data/lib/adhearsion/initializer/configuration.rb +321 -0
  56. data/lib/adhearsion/initializer/database.rb +60 -0
  57. data/lib/adhearsion/initializer/drb.rb +31 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/ldap.rb +57 -0
  60. data/lib/adhearsion/initializer/rails.rb +41 -0
  61. data/lib/adhearsion/initializer/xmpp.rb +42 -0
  62. data/lib/adhearsion/logging.rb +92 -0
  63. data/lib/adhearsion/tasks.rb +16 -0
  64. data/lib/adhearsion/tasks/database.rb +5 -0
  65. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  66. data/lib/adhearsion/tasks/generating.rb +20 -0
  67. data/lib/adhearsion/tasks/lint.rb +4 -0
  68. data/lib/adhearsion/tasks/testing.rb +37 -0
  69. data/lib/adhearsion/version.rb +33 -0
  70. data/lib/adhearsion/voip/asterisk.rb +4 -0
  71. data/lib/adhearsion/voip/asterisk/agi_server.rb +115 -0
  72. data/lib/adhearsion/voip/asterisk/commands.rb +1510 -0
  73. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  74. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  75. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  76. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  77. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  78. data/lib/adhearsion/voip/asterisk/manager_interface.rb +705 -0
  79. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1680 -0
  80. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +340 -0
  81. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  82. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  83. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  84. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  85. data/lib/adhearsion/voip/call.rb +497 -0
  86. data/lib/adhearsion/voip/call_routing.rb +64 -0
  87. data/lib/adhearsion/voip/commands.rb +9 -0
  88. data/lib/adhearsion/voip/constants.rb +39 -0
  89. data/lib/adhearsion/voip/conveniences.rb +18 -0
  90. data/lib/adhearsion/voip/dial_plan.rb +246 -0
  91. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  92. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  93. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  94. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +69 -0
  96. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  97. data/lib/adhearsion/voip/dsl/numerical_string.rb +115 -0
  98. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  99. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  100. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  101. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  102. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  103. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  104. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  105. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  106. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  107. data/lib/adhearsion/xmpp/connection.rb +61 -0
  108. data/lib/theatre.rb +151 -0
  109. data/lib/theatre/README.markdown +64 -0
  110. data/lib/theatre/callback_definition_loader.rb +84 -0
  111. data/lib/theatre/guid.rb +23 -0
  112. data/lib/theatre/invocation.rb +121 -0
  113. data/lib/theatre/namespace_manager.rb +153 -0
  114. data/lib/theatre/version.rb +2 -0
  115. metadata +241 -0
@@ -0,0 +1,17 @@
1
+ class Object
2
+ def metaclass
3
+ class << self
4
+ self
5
+ end
6
+ end
7
+
8
+ def meta_eval(&block)
9
+ metaclass.instance_eval &block
10
+ end
11
+
12
+ def meta_def(name, &block)
13
+ meta_eval do
14
+ define_method name, &block
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ class Numeric
2
+
3
+ def digit()
4
+ ahn_log.deprecation 'Please do not use Fixnum#digit() and Fixnum#digits() in the future! These will be deprecated soon'
5
+ self
6
+ end
7
+
8
+ def digits()
9
+ ahn_log.deprecation 'Please do not use Fixnum#digit() and Fixnum#digits() in the future! These will be deprecated soon'
10
+ self
11
+ end
12
+
13
+ end
@@ -0,0 +1,10 @@
1
+ module PseudoGuidGenerator
2
+ ##
3
+ # Generates a new 128-bit Globally Unique Identifier. It is a "pseudo" in that it does not adhere to the RFC which mandates
4
+ # that a certain section be reserved for a fragment of the NIC MAC address.
5
+ def new_guid(separator="-")
6
+ [8,4,4,4,12].map { |segment_length| String.random(segment_length) }.join(separator)
7
+ end
8
+ end
9
+
10
+ include PseudoGuidGenerator
@@ -0,0 +1,42 @@
1
+ class Module
2
+
3
+ ##
4
+ # In OOP, relationships between classes should be treated as *properties* of those classes. Often, in a complex OO
5
+ # architecture, you'll end up with many relationships that intermingle in monolithic ways, blunting the effectiveness of
6
+ # subclassing.
7
+ #
8
+ # For example, say you have an Automobile class which, in its constructor, instantiates a new Battery class and performs
9
+ # some operations on it such as calling an install() method. Let's also assume the Automobile class exposes a repair()
10
+ # method which uses a class-level method of Battery to diagnose your own instance of Battery. If the result of the
11
+ # diagnosis shows that the Battery is bad, the Automobile will instantiate a new Battery object and replace the old battery
12
+ # with the new one.
13
+ #
14
+ # Now, what if you wish to create a new Automobile derived from existing technology: a HybridAutomobile subclass. For this
15
+ # particular HybridAutomobile class, let's simply say the only difference between it and its parent is which kind of
16
+ # Battery it uses -- it requires its own special subclass of Battery. With Automobile's current implementation, its
17
+ # references to which Battery it instantiates and uses are embedded in the immutable method defintions. This
18
+ # HybridAutomobile needs to override which Battery its superclass' methods use and nothing else.
19
+ #
20
+ # For this reason, the Battery class which Automobile uses is semantically a property which others may want to override.
21
+ # In OOP theory, we define overridable properties in the form of methods and override those methods in the subclasses.
22
+ #
23
+ # This method exposes one method which creates human-readable semantics to defining these relationships as properties. It's
24
+ # used as follows:
25
+ #
26
+ # class Automobile
27
+ # relationship :battery => Battery
28
+ # relationship :chassis => Chassis
29
+ # # Other properties and instance methods here....
30
+ # end
31
+ #
32
+ # class HybridAutomobile < Automobile
33
+ # relationship :battery => HybridBattery
34
+ # end
35
+ #
36
+ def relationships(relationship_mapping)
37
+ relationship_mapping.each_pair do |class_name, class_object|
38
+ define_method(class_name) { class_object }
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,26 @@
1
+ class String
2
+
3
+ def unindent
4
+ gsub(/^\s*/,'')
5
+ end
6
+
7
+ def unindent!
8
+ gsub!(/^\s*/,'')
9
+ end
10
+
11
+ def self.random_char
12
+ case random_digit = rand(62)
13
+ when 0...10 then random_digit.to_s
14
+ when 10...36 then (random_digit + 55).chr
15
+ when 36...62 then (random_digit + 61).chr
16
+ end
17
+ end
18
+
19
+ def self.random(length_of_string=8)
20
+ Array.new(length_of_string) { random_char }.join
21
+ end
22
+
23
+ def nameify() downcase.gsub(/[^\w]/, '') end
24
+ def nameify!() replace nameify end
25
+
26
+ end
@@ -0,0 +1,96 @@
1
+ ##
2
+ # Implementation of a Thread-safe Hash. Works by delegating methods to a Hash behind-the-scenes after obtaining an exclusive # lock. Use exactly as you would a normal Hash.
3
+ #
4
+ class SynchronizedHash
5
+
6
+ def self.atomically_delegate(method_name)
7
+ class_eval(<<-RUBY, __FILE__, __LINE__)
8
+ def #{method_name}(*args, &block)
9
+ @lock.synchronize do
10
+ @delegate.send(#{method_name.inspect}, *args, &block)
11
+ end
12
+ end
13
+ RUBY
14
+ end
15
+
16
+ # Hash-related methods
17
+
18
+ atomically_delegate :[]
19
+ atomically_delegate :[]=
20
+ atomically_delegate :all?
21
+ atomically_delegate :any?
22
+ atomically_delegate :clear
23
+ atomically_delegate :collect
24
+ atomically_delegate :default
25
+ atomically_delegate :default=
26
+ atomically_delegate :delete
27
+ atomically_delegate :delete_if
28
+ atomically_delegate :detect
29
+ atomically_delegate :each
30
+ atomically_delegate :each_key
31
+ atomically_delegate :each_pair
32
+ atomically_delegate :each_value
33
+ atomically_delegate :each_with_index
34
+ atomically_delegate :empty?
35
+ atomically_delegate :entries
36
+ atomically_delegate :fetch
37
+ atomically_delegate :find
38
+ atomically_delegate :find_all
39
+ atomically_delegate :grep
40
+ atomically_delegate :has_key?
41
+ atomically_delegate :has_value?
42
+ atomically_delegate :include?
43
+ atomically_delegate :index
44
+ atomically_delegate :indexes
45
+ atomically_delegate :indices
46
+ atomically_delegate :inject
47
+ atomically_delegate :invert
48
+ atomically_delegate :key?
49
+ atomically_delegate :keys
50
+ atomically_delegate :length
51
+ atomically_delegate :map
52
+ atomically_delegate :max
53
+ atomically_delegate :member?
54
+ atomically_delegate :merge
55
+ atomically_delegate :merge!
56
+ atomically_delegate :min
57
+ atomically_delegate :partition
58
+ atomically_delegate :rehash
59
+ atomically_delegate :reject
60
+ atomically_delegate :reject!
61
+ atomically_delegate :replace
62
+ atomically_delegate :select
63
+ atomically_delegate :shift
64
+ atomically_delegate :size
65
+ atomically_delegate :sort
66
+ atomically_delegate :sort_by
67
+ atomically_delegate :store
68
+ atomically_delegate :to_hash
69
+ atomically_delegate :update
70
+ atomically_delegate :value?
71
+ atomically_delegate :values
72
+ atomically_delegate :values_at
73
+ atomically_delegate :zip
74
+
75
+ # Object-related methods
76
+
77
+ atomically_delegate :inspect
78
+ atomically_delegate :to_s
79
+ atomically_delegate :marshal_dump
80
+
81
+ def initialize(*args, &block)
82
+ @delegate = Hash.new(*args, &block)
83
+ @lock = Mutex.new
84
+ end
85
+
86
+ ##
87
+ # If you need to do many operations atomically (a la transaction), you can call this method and access the yielded Hash
88
+ # which can be safely modified for the duration of your block.
89
+ #
90
+ # @yield [Hash] the Hash on which you can safely operate during your block.
91
+ #
92
+ def with_lock(&block)
93
+ @lock.synchronize { yield @delegate }
94
+ end
95
+
96
+ end
@@ -0,0 +1,7 @@
1
+ require 'thread'
2
+ class Object
3
+ def synchronize(&block)
4
+ @mutex ||= Mutex.new
5
+ @mutex.synchronize &block
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ require 'yaml'
2
+
3
+ module Adhearsion
4
+
5
+ ##
6
+ # This class isn't yet tied into Adhearsion.
7
+ #
8
+ class HostDefinition
9
+
10
+ SUPPORTED_KEYS = [:host, :username, :password, :key, :name]
11
+
12
+ cattr_reader :definitions
13
+ @@definitions ||= []
14
+
15
+ class << self
16
+ def import_from_data_structure(local_definitions)
17
+ case local_definitions
18
+ when Array
19
+ local_definitions.each do |definition|
20
+ raise HostDefinitionException, "Unrecognized definition: #{definition}" unless definition.is_a?(Hash)
21
+ end
22
+ local_definitions.map { |definition| new definition }
23
+ when Hash
24
+ local_definitions.map do |(name,definition)|
25
+ new definition.merge(:name => name)
26
+ end
27
+ else
28
+ raise HostDefinitionException, "Unrecognized definition #{local_definitions}"
29
+ end
30
+ end
31
+
32
+ def import_from_yaml(yaml_string)
33
+ import_from_data_structure YAML.load(yaml_string)
34
+ end
35
+
36
+ def import_from_yaml_file(file)
37
+ import_from_yaml YAML.load_file(file)
38
+ end
39
+
40
+ def clear_definitions!
41
+ definitions.clear
42
+ end
43
+ end
44
+
45
+ attr_reader :name, :host, :username, :password, :key
46
+ def initialize(hash)
47
+ @host, @username, @password, @key, @name = hash.values_at(*SUPPORTED_KEYS)
48
+ @name ||= new_guid
49
+
50
+ unrecognized_keys = hash.keys - SUPPORTED_KEYS
51
+ raise HostDefinitionException, "Unrecognized key(s): #{unrecognized_keys.map(&:inspect).to_sentence}" if unrecognized_keys.any?
52
+ raise HostDefinitionException, "You must supply a password or key!" if username && !(password || key)
53
+ raise HostDefinitionException, "You must supply a username!" unless username
54
+ raise HostDefinitionException, 'You cannot supply both a password and key!' if password && key
55
+ raise HostDefinitionException, 'You must supply a host!' unless host
56
+
57
+ self.class.definitions << self
58
+ end
59
+
60
+ class HostDefinitionException < StandardError
61
+
62
+ end
63
+
64
+ end
65
+
66
+
67
+ end
@@ -0,0 +1,395 @@
1
+ module Adhearsion
2
+
3
+ mattr_accessor :status
4
+
5
+ class << self
6
+
7
+ ##
8
+ # Shuts down the framework.
9
+ #
10
+ def shutdown!
11
+ if self.status == :stopping
12
+ # This is the second shutdown request we've received while attempting
13
+ # to shut down gracefully. At this point, let's pull the plug...
14
+ ahn_log.warning "Shutting down immediately at #{Time.now}"
15
+ exit
16
+ end
17
+ ahn_log "Shutting down gracefully at #{Time.now}."
18
+ self.status = :stopping
19
+ Events.trigger_immediately :shutdown
20
+ Events.stop!
21
+ exit
22
+ end
23
+
24
+ end
25
+ class PathString < String
26
+
27
+ class << self
28
+
29
+ ##
30
+ # Will return a PathString for the application root folder to which the specified arbitrarily nested subfolder belongs.
31
+ # It works by traversing parent directories looking for the .ahnrc file. If no .ahnrc is found, nil is returned.
32
+ #
33
+ # @param [String] folder The path to the directory which should be a
34
+ # @return [nil] if the subdirectory does not belong to a parent Adhearsion app directory
35
+ # @return [PathString] if a directory is found
36
+ #
37
+ def from_application_subdirectory(folder)
38
+ folder = File.expand_path folder
39
+ ahn_rc = nil
40
+
41
+ until ahn_rc || folder == "/"
42
+ possible_ahn_rc = File.join(folder, ".ahnrc")
43
+ if File.exists?(possible_ahn_rc)
44
+ ahn_rc = possible_ahn_rc
45
+ else
46
+ folder = File.expand_path(folder + "/..")
47
+ end
48
+ end
49
+ ahn_rc ? new(folder) : nil
50
+ end
51
+ end
52
+
53
+ attr_accessor :component_path, :dialplan_path, :log_path
54
+
55
+ def initialize(path)
56
+ super
57
+ defaults
58
+ end
59
+
60
+ def defaults
61
+ @component_path = build_path_for "components"
62
+ @dialplan_path = dup
63
+ @log_path = build_path_for "logs"
64
+ end
65
+
66
+ def base_path=(value)
67
+ replace(value)
68
+ defaults
69
+ end
70
+
71
+ def using_base_path(temporary_base_path, &block)
72
+ original_path = dup
73
+ self.base_path = temporary_base_path
74
+ block.call
75
+ ensure
76
+ self.base_path = original_path
77
+ end
78
+
79
+ private
80
+ def build_path_for(path)
81
+ File.join(to_s, path)
82
+ end
83
+ end
84
+
85
+ class Initializer
86
+
87
+ class << self
88
+ def get_rules_from(location)
89
+ location = File.join location, ".ahnrc" if File.directory? location
90
+ File.exists?(location) ? YAML.load_file(location) : nil
91
+ end
92
+
93
+ def ahn_root=(path)
94
+ if Object.constants.include?("AHN_ROOT")
95
+ Object.const_get(:AHN_ROOT).base_path = File.expand_path(path)
96
+ else
97
+ Object.const_set(:AHN_ROOT, PathString.new(File.expand_path(path)))
98
+ end
99
+ end
100
+
101
+ def start(*args, &block)
102
+ new(*args, &block).start
103
+ end
104
+
105
+ def start_from_init_file(file, ahn_app_path)
106
+ return if defined?(@@started) && @@started
107
+ start ahn_app_path, :loaded_init_files => file
108
+ end
109
+
110
+ end
111
+
112
+ attr_reader :path, :daemon, :pid_file, :log_file, :ahn_app_log_directory
113
+
114
+ # Creation of pid_files
115
+ #
116
+ # - You may want to have Adhearsion create a process identification
117
+ # file when it boots so that a crash monitoring program such as
118
+ # Monit can reboot if necessary or so the init script can kill it
119
+ # for system shutdowns.
120
+ # - To have Adhearsion create a pid file in the default location (i.e.
121
+ # AHN_INSTALL_DIR/adhearsion.pid), supply :pid_file with 'true'. Otherwise
122
+ # one is not created UNLESS it is running in daemon mode, in which
123
+ # case one is created. You can force Adhearsion to not create one
124
+ # even in daemon mode by supplying "false".
125
+ def initialize(path=nil, options={})
126
+ @@started = true
127
+ @path = path
128
+ @daemon = options[:daemon] || ENV['DAEMON']
129
+ @pid_file = options[:pid_file].nil? ? ENV['PID_FILE'] : options[:pid_file]
130
+ @loaded_init_files = options[:loaded_init_files]
131
+ end
132
+
133
+ def start
134
+ self.class.ahn_root = path
135
+
136
+ Adhearsion.status = :starting
137
+
138
+ resolve_pid_file_path
139
+ resolve_log_file_path
140
+ daemonize! if should_daemonize?
141
+ switch_to_root_directory
142
+ catch_termination_signal
143
+ create_pid_file if pid_file
144
+ bootstrap_rc
145
+ initialize_log_file
146
+ load_all_init_files
147
+ init_datasources
148
+ init_components_subsystem
149
+ init_modules
150
+ init_events_subsystem
151
+ load_components
152
+ init_events_file
153
+
154
+ ahn_log "Adhearsion v#{Adhearsion::VERSION::STRING} initialized!"
155
+ Adhearsion.status = :running
156
+
157
+ trigger_after_initialized_hooks
158
+ join_important_threads
159
+
160
+ self
161
+ end
162
+
163
+ def default_pid_path
164
+ File.join AHN_ROOT, 'adhearsion.pid'
165
+ end
166
+
167
+ def resolve_pid_file_path
168
+ @pid_file = if pid_file.equal?(true) then default_pid_path
169
+ elsif pid_file then pid_file
170
+ elsif pid_file.equal?(false) then nil
171
+ # FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
172
+ else @pid_file = @daemon ? default_pid_path : nil
173
+ end
174
+ end
175
+
176
+ def resolve_log_file_path
177
+ @ahn_app_log_directory = AHN_ROOT + '/log'
178
+ @log_file = File.expand_path(ahn_app_log_directory + "/adhearsion.log")
179
+ end
180
+
181
+ def switch_to_root_directory
182
+ Dir.chdir AHN_ROOT
183
+ end
184
+
185
+ def catch_termination_signal
186
+ %w'INT TERM'.each do |process_signal|
187
+ trap process_signal do
188
+ Adhearsion.shutdown!
189
+ end
190
+ end
191
+ end
192
+
193
+ ##
194
+ # This step in the initialization process loads the .ahnrc in the given app folder. With the information in .ahnrc, we
195
+ # can continue the initialization knowing where certain files are specifically.
196
+ #
197
+ def bootstrap_rc
198
+ rules = self.class.get_rules_from AHN_ROOT
199
+
200
+ AHN_CONFIG.ahnrc = rules
201
+
202
+ # DEPRECATION: Check if the old paths format is being used. If so, abort and notify.
203
+ if rules.has_key?("paths") && rules["paths"].kind_of?(Hash)
204
+ paths = rules["paths"].each_pair do |key,value|
205
+ if value.kind_of?(Hash)
206
+ if value.has_key?("directory") || value.has_key?("pattern")
207
+ puts
208
+ puts *caller
209
+ puts
210
+
211
+ abort <<-WARNING
212
+ Deprecation Warning
213
+ -------------------
214
+ The (hidden) .ahnrc file in this app is of an older format and needs to be fixed.
215
+
216
+ There is a rake task to automatically fix it or you can do it manually. Note: it's
217
+ best if you do it manually so you can retain the YAML comments in your .ahnrc file.
218
+
219
+ The rake task is called "deprecations:fix_ahnrc_path_format".
220
+
221
+ To do it manually, find all entries in the "paths" section of your ".ahnrc" file
222
+ which look like the following:
223
+
224
+ paths:
225
+ key_name_could_be_anything:
226
+ directory: some_folder
227
+ pattern: *.rb
228
+
229
+ Note: the "models" section had this syntax before:
230
+
231
+ models:
232
+ directory: models
233
+ pattern: "*.rb"
234
+
235
+ The NEW syntax is as follows (using models as an example):
236
+
237
+ models: models/*.rb
238
+
239
+ This new format is much cleaner.
240
+
241
+ Adhearsion will abort until you fix this. Sorry for the incovenience.
242
+ WARNING
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ gems = rules['gems']
249
+ if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
250
+ gems.each_pair do |gem_name,properties_hash|
251
+ if properties_hash && properties_hash["version"]
252
+ gem gem_name, properties_hash["version"]
253
+ else
254
+ gem gem_name
255
+ end
256
+ if properties_hash
257
+ case properties_hash["require"]
258
+ when Array
259
+ properties_hash["require"].each { |lib| require lib }
260
+ when String
261
+ require properties_hash["require"]
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ def load_all_init_files
269
+ init_files_from_rc = AHN_CONFIG.files_from_setting("paths", "init").map { |file| File.expand_path(file) }
270
+ already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
271
+ (init_files_from_rc - already_loaded_init_files).each { |init| load init }
272
+ end
273
+
274
+ def init_datasources
275
+ require 'adhearsion/initializer/database.rb'
276
+ require 'adhearsion/initializer/ldap.rb'
277
+
278
+ DatabaseInitializer.start if AHN_CONFIG.database_enabled?
279
+ LdapInitializer.start if AHN_CONFIG.ldap_enabled?
280
+ end
281
+
282
+ def init_modules
283
+
284
+ require 'adhearsion/initializer/asterisk.rb'
285
+ require 'adhearsion/initializer/drb.rb'
286
+ require 'adhearsion/initializer/rails.rb'
287
+ require 'adhearsion/initializer/xmpp.rb'
288
+ # require 'adhearsion/initializer/freeswitch.rb'
289
+
290
+ AsteriskInitializer.start if AHN_CONFIG.asterisk_enabled?
291
+ DrbInitializer.start if AHN_CONFIG.drb_enabled?
292
+ RailsInitializer.start if AHN_CONFIG.rails_enabled?
293
+ XMPPInitializer.start if AHN_CONFIG.xmpp_enabled?
294
+ # FreeswitchInitializer.start if AHN_CONFIG.freeswitch_enabled?
295
+
296
+ end
297
+
298
+ def init_events_subsystem
299
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
300
+ if application_events_files.any?
301
+ Events.register_callback(:shutdown) do
302
+ ahn_log.events "Performing a graceful stop of events subsystem"
303
+ Events.framework_theatre.graceful_stop!
304
+ end
305
+ Events.framework_theatre.start!
306
+ else
307
+ ahn_log.events.warn 'No entries in the "events" section of .ahnrc. Skipping its initialization.'
308
+ end
309
+ end
310
+
311
+ def init_events_file
312
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
313
+ application_events_files.each do |file|
314
+ Events.framework_theatre.load_events_file file
315
+ end
316
+ end
317
+
318
+ def should_daemonize?
319
+ @daemon
320
+ end
321
+
322
+ def daemonize!
323
+ ahn_log "Daemonizing now! Creating #{pid_file}."
324
+ extend Adhearsion::CustomDaemonizer
325
+ daemonize log_file
326
+ end
327
+
328
+ def initialize_log_file
329
+ Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
330
+ file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
331
+
332
+ if should_daemonize?
333
+ Logging::AdhearsionLogger.outputters = file_logger
334
+ else
335
+ Logging::AdhearsionLogger.outputters << file_logger
336
+ end
337
+ Logging::DefaultAdhearsionLogger.redefine_outputters
338
+ end
339
+
340
+ def create_pid_file
341
+ if pid_file
342
+ File.open pid_file, 'w' do |file|
343
+ file.puts Process.pid
344
+ end
345
+
346
+ Events.register_callback :shutdown do
347
+ File.delete(pid_file) if File.exists?(pid_file)
348
+ end
349
+ end
350
+ end
351
+
352
+ def init_components_subsystem
353
+ @components_directory = File.expand_path "components"
354
+ if File.directory? @components_directory
355
+ Components.component_manager = Components::ComponentManager.new @components_directory
356
+ Kernel.send(:const_set, :COMPONENTS, Components.component_manager.lazy_config_loader)
357
+ Components.component_manager.globalize_global_scope!
358
+ Components.component_manager.extend_object_with(Theatre::CallbackDefinitionLoader, :events)
359
+ else
360
+ ahn_log.warn "No components directory found. Not initializing any components."
361
+ end
362
+ end
363
+
364
+ def load_components
365
+ if Components.component_manager
366
+ Components.component_manager.load_components
367
+ end
368
+ end
369
+
370
+ def trigger_after_initialized_hooks
371
+ Events.trigger_immediately :after_initialized
372
+ end
373
+
374
+ ##
375
+ # This method will block Thread.main() until calling join() has returned for all Threads in IMPORTANT_THREADS.
376
+ # Note: IMPORTANT_THREADS won't always contain Thread instances. It simply requires the objects respond to join().
377
+ #
378
+ def join_important_threads
379
+ # Note: we're using this ugly accumulator to ensure that all threads have ended since IMPORTANT_THREADS will almost
380
+ # certainly change sizes after this method is called.
381
+ index = 0
382
+ until index == IMPORTANT_THREADS.size
383
+ begin
384
+ IMPORTANT_THREADS[index].join
385
+ rescue => e
386
+ ahn_log.error "Error after join()ing Thread #{thread.inspect}. #{e.message}"
387
+ ensure
388
+ index = index + 1
389
+ end
390
+ end
391
+ end
392
+
393
+ class InitializationFailedError < StandardError; end
394
+ end
395
+ end