summaryrefslogtreecommitdiffstats
path: root/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
diff options
context:
space:
mode:
Diffstat (limited to 'EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java')
-rw-r--r--EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java271
1 files changed, 271 insertions, 0 deletions
diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
new file mode 100644
index 000000000..06ff33f70
--- /dev/null
+++ b/EssentialsUpdate/src/f00f/net/irc/martyr/services/AutoReconnect.java
@@ -0,0 +1,271 @@
+package f00f.net.irc.martyr.services;
+
+import java.io.IOException;
+
+import f00f.net.irc.martyr.IRCConnection;
+import f00f.net.irc.martyr.InCommand;
+import f00f.net.irc.martyr.GenericAutoService;
+import f00f.net.irc.martyr.State;
+import f00f.net.irc.martyr.clientstate.ClientState;
+import f00f.net.irc.martyr.commands.QuitCommand;
+
+/**
+ * <p>AutoReconnect performs the job of reconnecting to the server, if
+ * the connection is terminated unexpectedly. AutoReconnect
+ * will try to reconnect 5 times, and then give up. If AutoReconnect
+ * intercepts a QUIT command before the state change that is issued by
+ * us (bounced back from the server) then it will not try to
+ * reconnect.</p>
+ *
+ * <p>Note that AutoReconnect has no play in nick negotiation, and as
+ * such, a failed nick negotiation does not count as a connection
+ * retry.</p>
+ *
+ * <p>Testing note: Does a server send a QUIT before a forceful
+ * removal from the network? Should AutoReconnect not intercept
+ * QUITs? Certainly not all servers send QUITs even when you QUIT on
+ * your own; this class should be put into a command-out chain as well.</p>
+ */
+public class AutoReconnect extends GenericAutoService
+{
+ //static Logger log = Logger.getLogger(AutoReconnect.class);
+
+ private int attempt;
+ private int maxAttempts;
+ private int sleepTime;
+ private boolean disableOnQuit;
+
+ // How many times to try reconnecting?
+ public static final int DEFAULT_MAX_ATTEMPTS = 5;
+ // TODO This is a rather simplistic method, personally I would like to
+ // see a version of this class that implements a backoff algorithm.
+
+ // If we tried to connect, but failed, how long do we wait until we
+ // try again (ms)?
+ public static final int DEFAULT_CONNECT_SLEEPTIME = 1000;
+
+ // If we get a QUIT command from the server notifying us that we have
+ // QUIT, then self-disable so that we don't reconnect.
+ public static final boolean DEFAULT_DISABLE_ON_QUIT = true;
+
+ /**
+ * @param connection The IRCConnection
+ * @param maxAttempts The number of tries to do before giving up
+ * @param sleepBetween Milliseconds to sleep between retries
+ * @param disableOnQuit Automatically disable on quit event
+ */
+ public AutoReconnect( IRCConnection connection, int maxAttempts,
+ int sleepBetween, boolean disableOnQuit )
+ {
+ super( connection );
+
+ this.disableOnQuit = disableOnQuit;
+ this.maxAttempts = maxAttempts;
+ this.sleepTime = sleepBetween;
+ this.attempt = 0;
+
+ enable();
+ }
+
+ public AutoReconnect( IRCConnection connection, int maxAttempts,
+ int sleepBetween )
+ {
+ this( connection, maxAttempts, sleepBetween, DEFAULT_DISABLE_ON_QUIT );
+ }
+
+ /**
+ * Initializes with reasonable defaults.
+ *
+ * @param connection Connection we are associated with
+ */
+ public AutoReconnect( IRCConnection connection )
+ {
+ this( connection, DEFAULT_MAX_ATTEMPTS, DEFAULT_CONNECT_SLEEPTIME );
+ }
+
+ /**
+ * Attempts to connect, returning only when a connection has been made
+ * or repeated connections have failed.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * */
+ public void go( String server, int port )
+ {
+ doConnectionLoop( server, port );
+ }
+
+ /**
+ * Attempts a single connection to the server. The connection is done
+ * by asking the client state what server and port we are supposed to
+ * be connected to, and then calling IRCConnection.connect(...).
+ *
+ * @throws IOException if we could not connect successfully
+ * */
+ protected void connect()
+ throws IOException
+ {
+ ClientState cstate = getConnection().getClientState();
+ connect( cstate.getServer(), cstate.getPort() );
+ }
+
+ /**
+ * Attempts a single connection to the server.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * @throws IOException if we could not connect successfully
+ * */
+ protected void connect( String server, int port )
+ throws IOException
+ {
+ getConnection().connect( server, port );
+ }
+
+ protected void updateState( State state )
+ {
+ //log.debug("AutoReconnect: Update with state " + state);
+ if( state == State.UNCONNECTED )
+ {
+ // This should only happen if we were connected and then
+ // disconnected. Try connecting.
+
+ // failedToConnect() prevents insane-reconnecting loop if
+ // we never registered, however, it introduces a delay if we
+ // were registered properly previously
+ if (failedToConnect(null))
+ doConnectionLoop();
+ } else if( state == State.REGISTERED ) {
+ this.attempt = 0;
+ }
+
+ //log.debug("AutoReconnect: Returned from " + state);
+ }
+
+ /**
+ * Calls connect() followed by failedToConnect(...) until a connection
+ * is made. Gets the server and port from the client state, implying
+ * that a connection was already attempted before.
+ * */
+ protected void doConnectionLoop()
+ {
+ // Tell called proc to use client state info
+ doConnectionLoop( null, -1 );
+ }
+
+ /**
+ * Calls connect() followed by failedToConnect(...) until a connection
+ * is made, or the service is disabled.
+ *
+ * @param server Server to connect to
+ * @param port Port to connect to
+ * */
+ protected void doConnectionLoop( String server, int port )
+ {
+ boolean keeptrying = true;
+
+ while( keeptrying && enabled )
+ {
+ Exception error = null;
+ try
+ {
+ if( server == null )
+ {
+ // Try getting the info from the client state
+ connect();
+ }
+ else
+ {
+ connect( server, port );
+ }
+ keeptrying = false;
+ }
+ catch( Exception e )
+ {
+ error = e;
+ keeptrying = true;
+ }
+
+ if( keeptrying )
+ {
+ keeptrying = failedToConnect( error );
+ }
+ }
+ }
+
+ /**
+ * Called when the final failure has occurred. Default implementation
+ * does nothing.
+ * */
+ protected void finalFailure()
+ {
+ //log.debug("AutoReconnect: Final failure.");
+ this.attempt = 0;
+ }
+
+ /**
+ * Called when a failure to connect occurs. This method should do
+ * whatever needs to be done between connection attempts; usually this
+ * will be sleeping the current thread and checking if we should stop
+ * trying to reconnect.
+ *
+ * @param error Exception that caused the failure
+ * @return true if another attempt at connecting should occur
+ * */
+ protected boolean failedToConnect( Exception error )
+ {
+ //log.debug("AutoReconnect: Error connecting: " + error);
+
+ this.attempt++;
+
+ // abort if we've tried too many times
+ if( attempt >= maxAttempts )
+ {
+ //log.debug("AutoReconnect: Tried " + attempt + " times, giving up.");
+ finalFailure();
+ return false;
+ }
+
+ // Well, try again damnit!
+ // Sleep for a bit first.
+ try
+ {
+ Thread.sleep( sleepTime );
+ }
+ catch( InterruptedException ie )
+ {
+ // And we're going to do what?
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * AutoReconnect will disable itself if it sees a quit command
+ * generated by returned from the server. This usually happens just
+ * before the server terminates the connection. Note that this would
+ * really be better if we could intercept messages on their way out,
+ * but Martyr doesn't do that.
+ */
+ protected void updateCommand( InCommand command )
+ {
+ if( disableOnQuit
+ && command instanceof QuitCommand
+ && ((QuitCommand)command).isOurQuit(getConnection().getClientState()) )
+ {
+ //log.debug("AutoReconnect: Disabling due to receiving own QUIT.");
+ disable();
+ }
+ }
+
+ public String toString()
+ {
+ return "AutoReconnect [" + attempt + "]";
+ }
+
+}
+
+
+
+