# encoding: utf-8 require 'blather' require 'active_support/core_ext/class/attribute_accessors' module Adhearsion module Rayo class Initializer cattr_accessor :config, :client, :dispatcher, :attempts self.attempts = 0 class << self def init self.config = Adhearsion.config.core username = self.config.username if (self.config.type || :xmpp) == :xmpp username = Blather::JID.new username username = Blather::JID.new username.node, username.domain, resource unless username.resource username = username.to_s end connection_options = { :username => username, :password => self.config.password, :connection_timeout => self.config.connection_timeout, :host => self.config.host, :port => self.config.port, :certs => self.config.certs_directory, :root_domain => self.config.root_domain } self.client = Adhearsion.client_with_connection self.config.type, connection_options # Tell the connection that we are ready to process calls. Events.register_callback :after_initialized do connection.ready! end # When quiescence is requested, change our status to "Do Not Disturb" # This should prevent the telephony engine from sending us any new calls. Events.register_callback :quiesced do connection.not_ready! if connection.connected? end # Make sure we stop everything when we shutdown Events.register_callback :shutdown do client.stop end # Handle events from connection via events system self.client.register_event_handler do |event| handle_event event end Events.rayo Adhearsion::Rayo::Connection::Connected do |event| logger.info "Connected to Rayo server" self.attempts = 0 end Events.rayo Adhearsion::Event::Offer do |offer| dispatch_offer offer end Events.rayo proc { |e| e.respond_to?(:source) }, :source do |event| event.source.trigger_event_handler event end Events.rayo proc { |e| e.respond_to?(:target_call_id) }, :target_call_id do |event| dispatch_call_event event end end def run connect end def connect return unless Process.state_name == :booting m = Mutex.new blocker = ConditionVariable.new Events.rayo Adhearsion::Rayo::Connection::Connected do Adhearsion::Process.booted m.synchronize { blocker.broadcast } end Events.shutdown do logger.info "Shutting down while connecting. Breaking the connection block." m.synchronize { blocker.broadcast } end Adhearsion::Process.important_threads << Thread.new do catching_standard_errors { connect_to_server } end # Wait for the connection to establish m.synchronize { blocker.wait m } throw :boot_aborted if self.attempts >= self.config.reconnect_attempts end def connect_to_server logger.info "Starting connection to server" client.run rescue Adhearsion::Rayo::DisconnectedError => e # We only care about disconnects if the process is up or booting return unless [:booting, :running].include? Adhearsion::Process.state_name Adhearsion::Process.reset unless Adhearsion::Process.state_name == :booting self.attempts += 1 if self.attempts >= self.config.reconnect_attempts logger.fatal "Connection lost. Connection retry attempts exceeded." Adhearsion::Process.stop! return end logger.error "Connection lost. Attempting reconnect #{self.attempts} of #{self.config.reconnect_attempts}" sleep self.config.reconnect_timer retry rescue Adhearsion::ProtocolError => e logger.fatal "The connection failed due to a protocol error: #{e.name}." raise e end def dispatch_offer(offer) catching_standard_errors do call = Call.new(offer) Adhearsion.active_calls << call case Adhearsion::Process.state_name when :booting, :rejecting logger.info "Declining call because the process is not yet running." call.reject :decline when :running, :stopping Adhearsion.router.handle call else call.reject :error end end end def dispatch_call_event(event) call = Adhearsion.active_calls[event.target_call_id] if call && call.alive? call.async.deliver_message event else logger.warn "Event received for inactive call #{event.target_call_id}: #{event.inspect}" end end def handle_event(event) Events.trigger :rayo, event case event when Adhearsion::Event::Asterisk::AMI Events.trigger :ami, event end end def resource [machine_identifier, ::Process.pid].join '-' end def machine_identifier Adhearsion::Process.fqdn rescue SocketError Socket.gethostname end def connection client.connection end end end end end