1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
require 'xmpp4r/stream'
module Jabber
module Reliable
class Connection < Jabber::Client
def initialize(full_jid, config)
super(full_jid)
@servers = config[:servers]
@port = config[:port] || 5222
@max_retry = config[:max_retry] || 30
@retry_sleep = config[:retry_sleep] || 2
if(@servers.nil? or @servers.empty?)
@servers = [@jid.domain]
end
end
def connect
retry_count = 0
server_to_use = nil
server_pool = @servers.dup.sort{ rand <=> rand }
begin
server_to_use = server_pool.shift
server_pool.push(server_to_use)
Jabber::debuglog "timeout will be: #{@retry_sleep.to_f}"
Timeout.timeout(@retry_sleep.to_f){
Jabber::debuglog "trying to connect to #{server_to_use}"
super(server_to_use, @port)
}
Jabber::debuglog self.jid.to_s + " connected to " + server_to_use.to_s
Jabber::debuglog "out of possible servers " + @servers.inspect
rescue Exception, Timeout::Error => e
Jabber::warnlog "#{server_to_use} error: #{e.inspect}. Will attempt to reconnect in #{@retry_sleep}"
sleep(@retry_sleep.to_f)
if(retry_count >= @max_retry.to_i)
Jabber::warnlog "reached max retry count on exception, failing"
raise e
end
retry_count += 1
retry
end
end
end
class Listener
def initialize(full_jid, password, config, &block)
@on_message_block = block
@full_jid = full_jid
@config = config
@password = password
@max_retry = config[:max_retry] || 30
end
def setup_connection
@connection = Connection.new(@full_jid, @config)
if @on_message_block
@connection.add_message_callback(&@on_message_block)
else
@connection.add_message_callback do |msg|
self.on_message(msg)
end
end
#We could just reconnect in @connection.on_exception,
#but by raising into this seperate thread, we avoid growing our stack trace
@reconnection_thread = Thread.new do
first_run = true
begin
self.start unless first_run
loop do
sleep(1)
Thread.pass
end
rescue => e
first_run = false
retry
end
end
@exception_handlers = []
@connection.on_exception do |e, connection, where_failed|
self.run_exception_handlers(e, connection, where_failed)
end
end
def run_exception_handlers(e, connection, where_failed)
@exception_handlers.each do |ex_handler|
ex_handler.call(e, connection, where_failed)
end
if where_failed == :sending
@message_to_send_on_reconnect = @message_now_sending
end
if where_failed != :close && !@connection.is_connected?
@reconnection_thread.raise(e)
end
end
def add_exception_handler(&block)
@exception_handlers << block
end
def start
setup_connection unless @connection
connect
auth
send_presence
if @message_to_send_on_reconnect
send_message(@message_to_send_on_reconnect)
end
@message_to_send_on_reconnect = nil
end
#Stop the listener. (close the connection)
def stop
@connection.close if @connection and @connection.is_connected?
@connection = nil
end
def connect
@connection.connect
end
def auth
@connection.auth(@password)
end
def send_presence
presence_message = @config[:presence_message]
if presence_message && !presence_message.empty?
@connection.send(Jabber::Presence.new.set_show(:chat).set_status(presence_message))
end
end
#TODO: test and fix situation where we get disconnected while sending but then successfully reconnect
# (and make sure in such cases we resent)
def send_message(message)
unless @connection
raise ::ArgumentError, "Can't send messages while listener is stopped. Plase 'start' the listener first."
end
retry_count = 0
begin
while(not @connection.is_connected?)
#wait
Thread.pass
end
@message_now_sending = message
@connection.send(message)
return true #true, message was sent
rescue => e
if e.is_a?(Interrupt)
raise e
end
if(retry_count > @max_retry.to_i)
Jabber::debuglog "reached max retry count on message re-send, failing"
raise e
end
retry_count += 1
Jabber::debuglog "retrying message send.." + e.inspect
retry
end
end
end
end
end
|